J.BF Story

[Apache - PHP] URL Router 구현하기 본문

BackEnd/Apache

[Apache - PHP] URL Router 구현하기

J.BF 2022. 7. 21. 23:31

 

Windows 10
Apache 2.4
PHP 7

 

1. Apache .htaccess 설정

우선 .htaccess 파일을 사용할 수 있도록 Apache를 설정한다.

설정 방법은 다음 링크를 참고한다.

[Apache] .htaccess 설정

 

[Apache] .htaccess 설정

원하는 디렉터리에서 .htaccess 파일을 생성하여 해당 디렉터리의 하위 디렉터리까지 영향을 줄 수 있다. 설정 방법 1. Apache의 설정 파일 수정 'httpd.conf'파일에서 .htaccess가 rewrite할 수 있게 설정한

jbf-story.tistory.com

 

2. Apache mod_rewrite 모듈 설정

 웹 URL을 규칙을 변경하는 모듈인 'mod_rewrite'을 Apache에서 활성화한다.

다음과 같이 'C:\Apache24\conf\http.conf' 파일에서 'mod_rewrite'의 주석을 해제한다.

LoadModule rewrite_module modules/mod_rewrite.so

 

3. Apache 재시작

Apache를 재시작하여 변경한 설정을 반영한다.

 

4. Router을 적용할 디렉터리에 .htaccess 파일 생성

Router을 설정을 적용할 디렉터리에 .htaccess 파일을 생성한다.

DirectoryIndex index.php

# enable apache rewrite engine
RewriteEngine on

# set your rewrite base
# Edit this in your init method too if you script lives in a subfolder
RewriteBase /

# Deliver the folder or file directly if it exists on the server
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
 
# Push every request to index.php
RewriteRule ^(.*)$ index.php [QSA]

** 여기서 htdocs로부터의 목표 디렉터리의 상대 경로에 따라 RewriteBase을 설정해준다.

  ex1) C:\Apache24\htdocs => RewriteBase /

  ex2) C:\Apache24\htdocs\test_site => RewriteBase /test_stie

 

5. PHP Router 구현

Router을 구현할 때 참고한 코드는 다음과같다.

https://github.com/steampixel/simplePHPRouter

 

GitHub - steampixel/simplePHPRouter: This is a simple and small single class PHP router that can handel the whole url routing fo

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

github.com

간단히 구성한 폴더 구조는 다음과 같다.

  • assets: 웹페이지에 사용되는 정적 자원(이미지, 문서) 디렉터리
  • components: 컴포넌트 디렉터리
  • data: 데이터 디렉터리
  • fonts: 폰트 파일 디렉터리
  • index.php: 시작 페이지. URL route 설정하는데 사용
  • routes: 웹페이지 디렉터리
  • src: 소스(css, js) 디렉터리
  • utils: php 유틸리티 디렉터리

여기서 'Router.php'를 다음과 같이 작성한다.

<?php

class Router {

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

  /**
    * Function used to add a new route
    * @param string $expression    Route string or expression
    * @param callable $function    Function to call if route with allowed method is found
    * @param string|array $method  Either a string of allowed method or an array with string values
    *
    */
  public static function add($method, $expression, $function){
    array_push(self::$routes, Array(
      'expression' => $expression,
      'function' => $function,
      'method' => $method
    ));
  }

  public static function getAll(){
    return self::$routes;
  }

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

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

  public static function run($basepath = '', $case_matters = false, $trailing_slash_matters = false, $multimatch = false) {

    // The basepath never needs a trailing slash
    // Because the trailing slash will be added using the route expressions
    $basepath = rtrim($basepath, '/');

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

    $path = '/';

    // If there is a path available
    if (isset($parsed_url['path'])) {
      // If the trailing slash matters
  	  if ($trailing_slash_matters) {
  		  $path = $parsed_url['path'];
  	  } else {
        // If the path is not equal to the base path (including a trailing slash)
        if($basepath.'/'!=$parsed_url['path']) {
          // Cut the trailing slash away because it does not matters
          $path = rtrim($parsed_url['path'], '/');
        } else {
          $path = $parsed_url['path'];
        }
  	  }
    }

  	$path = urldecode($path);

    // Get current request method
    $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'].'$';

      // Check path match
      if (preg_match('#'.$route['expression'].'#'.($case_matters ? '' : 'i').'u', $path, $matches)) {
        $path_match_found = true;

        // Cast allowed method to array if it's not one already, then run through all methods
        foreach ((array)$route['method'] as $allowedMethod) {
            // Check method match
          if (strtolower($method) == strtolower($allowedMethod)) {
            array_shift($matches); // Always remove first element. This contains the whole string

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

            if($return_value = call_user_func_array($route['function'], $matches)) {
              echo $return_value;
            }

            $route_match_found = true;

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

      // Break the loop if the first found route is a match
      if($route_match_found&&!$multimatch) {
        break;
      }

    }

    // No matching route was found
    if (!$route_match_found) {
      // But a matching path exists
      if ($path_match_found) {
        if (self::$methodNotAllowed) {
          call_user_func_array(self::$methodNotAllowed, Array($path,$method));
        }
      } else {
        if (self::$pathNotFound) {
          call_user_func_array(self::$pathNotFound, Array($path));
        }
      }

    }
  }

}

 

'index.php' 예제 코드는 다음과 같다. 'routes' 디렉터리에 있는 페이지들의 URL route을 설정해준다.

<?php

// common setting
include('app/utils/router.php');
date_default_timezone_set('Asia/Seoul');
define('BASEPATH','/');


// route setting
Router::add('GET', '/',function() {
    include('routes/test_page/index.php');
});

Router::add('GET', '/test_page2',function() {
    include('routes/test_page2/index.php');
});

Router::add('GET', '/test_page2/sub_page1',function() {
    include('routes/test_page2/sub_page1.php');
});

Router::add('GET', '/test_page2/sub_page2',function() {
    include('routes/test_page2/sub_page2.php');
});

Router::run(BASEPATH);

?>

** 여기서 'BASEPATH'를 .htaccess의 'RewriteBase' 설정할 때와 동일하게 설정한다.

 

6. Router 작동 확인

URL route을 등록한 페이지들을 실제로 웹브라우저에 URL을 입력하여 작동을 확인한다.

http://localhost
http://localhost/test_page2
http://localhost/test_page2/sub_page1
http://localhost/test_page2/sub_page2

 

'BackEnd > Apache' 카테고리의 다른 글

[Apache] .htaccess 설정  (0) 2022.06.05
Comments