PHP

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

teamnova 2021. 3. 13. 13:00
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를 구축할수도 있습니다.