본문 바로가기
PHP

[PHP] 간단한 URL Router 구축해보기

by teamnova 2021. 3. 13.
728x90

 

이번 포스팅에서 다룰 내용은 PHP로 간단한 URL Router을 작성해보려고 합니다.

 

이전 PDO를 이용한 CRUD 코드를 Router을 통해서 간단히 연결해 보겠습니다.

 

사용된 모든 코드는 스틱코드 포스팅에 업로드 하였습니다.

stickode.com/detail.html?no=1925

 

스틱코드

 

stickode.com

 

라우터에 사용된 코드는

github.com/steampixel/simplePHPRouter/tree/master

 

steampixel/simplePHPRouter

This is a simple and small single class PHP router that can handel the whole url routing for your project. - steampixel/simplePHPRouter

github.com

에서 참고하였고 일부 수정되었습니다.

 

php라우팅을 적용하려면 우선 설치된 웹서버 Config파일 세팅이 선행되어야합니다.

 

설정후에 반드시 웹서버를 재시작 해주세요. https로 redirect 하는 세팅은 따로 적용하셔야합니다.

 

nginx 의 경우 nginx.conf 파일을 편집해줍니다.

server {
    listen 80;
    server_name example.com;
    index index.php;
    root /path/to/public;

    location / {
        try_files $uri /index.php$is_args$args;    ## 이부분을 제외한 다른부분은 각 서버 환경에 맞게 작성
    }

    location ~ \.php {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        fastcgi_index index.php;
        fastcgi_pass 127.0.0.1:9000;
    }
}

Apache 의 경우

 

Apache mod_rewrite모듈이 설치되고 활성화되어 있는지 확인하십시오 . 활성화 mod_rewrite하려면 터미널에 다음 명령을 입력 할 수 있습니다.

sudo a2enmod rewrite sudo a2enmod actions

.htaccess및 index.php파일이 동일한 공개 액세스 가능한 디렉토리에 있는지 확인하십시오 . .htaccess파일이 코드를 포함해야합니다 :

RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d 
RewriteRule ^ index.php [QSA,L]

 .htaccess파일은 URL 재 작성  필요합니다.

Apache mod_rewrite모듈 을 활성화 하고 가상 호스트가 다시 쓰기 규칙을 사용할 수 AllowOverride있도록 옵션으로 구성되어 있는지 확인하십시오 .htaccess. 이렇게하려면 파일 /etc/apache2/apache2.conf을 루트 권한으로 편집기에서 열어야합니다.

 

apache2.conf 예시

<Directory /var/www/>
	Options Indexes FollowSymLinks 
    AllowOverride All 
    Require all granted 
</Directory>

마지막으로 Apache 구성을 다시로드해야합니다. Apache 웹 서버를 다시 시작하려면 다음을 입력하십시오.

sudo service apache2 restart

 

 

 

이제 php 코드를 작성하겠습니다.

 

URL 라우팅에 이용할  Router 클래스 코드입니다.

 

Router.php

<?php


class Router
{
    private static $routes = Array();
    private static $pathNotFound = null;
    private static $methodNotAllowed = null;

    public static function add($method, $expression,  $function){
        array_push(self::$routes,Array(
            'expression' => $expression,
            'function' => $function,
            'method' => $method
        ));
    }

    public static function pathNotFound($function){
        self::$pathNotFound = $function;
    }

    public static function methodNotAllowed($function){
        self::$methodNotAllowed = $function;
    }

    public static function run($basepath = '/'){

        // Parse current url
        $parsed_url = parse_url($_SERVER['REQUEST_URI']);//Parse Uri

        if(isset($parsed_url['path'])){
            $path = $parsed_url['path'];
        }else{
            $path = '/';
        }

        // 현재 리퀘스트 메서드 확인(get,post...ect)
        $method = $_SERVER['REQUEST_METHOD'];

        $path_match_found = false;

        $route_match_found = false;

        foreach(self::$routes as $route){

            // If the method matches check the path

            // Add basepath to matching string
            if($basepath!=''&&$basepath!='/'){
                $route['expression'] = '('.$basepath.')'.$route['expression'];
            }

            // Add 'find string start' automatically
            $route['expression'] = '^'.$route['expression'];

            // Add 'find string end' automatically
            $route['expression'] = $route['expression'].'$';

            // echo $route['expression'].'<br/>';

            // Check path match
            if(preg_match('#'.$route['expression'].'#',$path,$matches)){

                $path_match_found = true;

                // Check method match
                if(strtolower($method) == strtolower($route['method'])){

                    array_shift($matches);// Always remove first element. This contains the whole string

                    if($basepath!=''&&$basepath!='/'){
                        array_shift($matches);// Remove basepath
                    }

                    call_user_func_array($route['function'], $matches);

                    $route_match_found = true;

                    // Do not check other routes
                    break;
                }
            }
        }

        // No matching route was found
        if(!$route_match_found){

            // But a matching path exists
            if($path_match_found){
                header("HTTP/1.0 405 Method Not Allowed");
                if(self::$methodNotAllowed){
                    call_user_func_array(self::$methodNotAllowed, Array($path,$method));
                }
            }else{
                header("HTTP/1.0 404 Not Found");
                if(self::$pathNotFound){
                    call_user_func_array(self::$pathNotFound, Array($path));
                }
            }

        }

    }
}

코드를 간단하게 설명하면 추가되어있는 URL, 리퀘스트메서드(get,post,update...etc)를 확인후 

파라미터, 정규식등확인하여 값을 파싱후 콜백함수를 리턴하도록 되어있습니다.

모든 경로를 순차적으로 라우터를 탐색하는 단순한 구조로 되어있습니다.

 

다음은 라우터가 실행되고 동작하는 index.php 파일입니다.

 

index.php

<?php
// Include router class
require   'Router.php';
//echo "hi";


// 일반적인 홈경로
Router::add('get','/',function(){
        echo '라우터 홈입니다.';
    });

//  static html file 테스트
Router::add('get','/test.html',function(){
    echo 'Hello from test.html';
});

// Post로 요청 보내기 테스트 하는 페이지
Router::add('get','/contact-form',function(){
    echo '<form method="post"><input type="text" name="test" /><input type="submit" value="send" /></form>';
});

// Post 경로로 들어오는 값 확인하기 테스트
Router::add('post','/contact-form',function(){
    echo '보낸 데이터 확인:<br/>';
    print_r($_POST);
});


// Accept only numbers as parameter. Other characters will result in a 404 error
Router::add('get','/foo/([a-z-0-9-]+)/bar/([0-9]+)',function($var1,$var2){
    echo '1번째 파라미터 :'.$var1.' 2번째 파라미터: '.$var2;
});






// idx(숫자, $var1) 값을 통해 유저 정보를 조회
Router::add('get','/user/([0-9-]+)',function($var1){
    require_once 'select_from_idx.php';
});

// 유저 정보를 테이블에 저장
Router::add('post','/user',function(){
    require_once 'insert.php';
});


Router::run();

 

가장 하단의 요청 2개를 제외하고 모두 웹 브라우저에서 테스트 가능합니다.

 

예를 들면 다음과 같이 확인이 가능합니다.

 

URL Routing Test

 

 

 

 

이제 이전 작성글인 stickode.tistory.com/19?category=921753 을 활용하여 

 

[PHP] PDO를 활용한 CRUD 구현

이번 포스팅에서 다룰 내용은 PHP PDO를 이용한 CRUD 구현입니다. PDO(PHP Data Objects)란 여러가지 데이터베이스를 제어하는 방법을 표준화시킨 것으로 데이터베이스를 처리하기위한 여러 기능을 제

stickode.tistory.com

POST 를 통한 유저 정보 추가, GET 요청을 통해 특정 idx 유저정보를 확인해보겠습니다.

 

dbconfig.php

 

<?php

return [
    'server'=>'서버 ip',
    'name'=>'DB 이름',
    'username'=>'DB 유저 명',
    'password'=>'DB 유저 비밀번호',
    'charset'=>'utf8mb4'
];

dbconnect.php

<?php
//echo __DIR__;
$dbconfig = require_once __DIR__.'/dbconfig.php';
//echo $dbconfig['server'];

try {
    return new PDO(
        "mysql:host=" . $dbconfig['server'] . ";dbname=" . $dbconfig['name'] . ";
        charset=" . $dbconfig['charset'], $dbconfig['username'], $dbconfig['password'],
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
        ]
    );
} catch (Exception $e) {
    echo $e->getMessage();
    // echo 'db connection error';
}

 

insert.php

 

<?php

$pdo = require_once __DIR__.'/dbconnect.php';

$data =json_decode(file_get_contents('php://input'), true);
try {
//    if(!$this->pdo->inTransaction()) // 필요시 트랜잭션
//        $this->pdo->beginTransaction();
    $col1 = $data['id'];
    $col2 = $data['pwd'];//패스워드
    $col3 = $data['nick'];
    $col2 = password_hash($col2,PASSWORD_BCRYPT);
//    echo $col1;
    $sql = "INSERT INTO user (id,password,nick) VALUES (?, ?,?)";
    $stmt = $pdo->prepare($sql);
    $stmt->execute(array($col1,$col2,$col3));
    $last_idx = $pdo->lastInsertId();
    echo "insert ok : inserted idx is ".$last_idx;
//    if($this->pdo->inTransaction())  // 필요시 트랜잭션
//        $this->pdo->commit();
} catch (Exception $e){
    if($pdo->inTransaction())
        $pdo->rollBack();
    echo $e->getMessage();
}

 

 

포스트맨을 통해 테스트 해보았습니다.

 

select_from_idx.php

 

<?php
$pdo =  require_once __DIR__.'/dbconnect.php';




$sql ="SELECT id,nick FROM user WHERE idx=?  ";
try {
    $stmt = $pdo->prepare($sql);
    $stmt->execute(array($var1));

    if ($stmt->rowCount()>0) { //
        $result = $stmt->fetchAll();
        echo json_encode($result,JSON_UNESCAPED_UNICODE);

    }else{ // 결과값 없음.
        echo "no user";
    }
} catch (Exception $e){
    if($pdo->inTransaction()) // 트랜잭션과 연관된 경우 롤백처리.
        $pdo->rollBack();
    echo $e->getMessage();
}

 

생성된 유저정보를 get요청으로 확인해보겠습니다.

 

 

아까 입력한 유저정보가 잘 보여지는것을 확인 가능합니다.

 

이런식으로 라우터를 통해 REST API를 구축할수도 있습니다.