J.BF Story
[Apache - PHP] URL Router 구현하기 본문
Windows 10
Apache 2.4
PHP 7
1. Apache .htaccess 설정
우선 .htaccess 파일을 사용할 수 있도록 Apache를 설정한다.
설정 방법은 다음 링크를 참고한다.
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
간단히 구성한 폴더 구조는 다음과 같다.
- 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