PHP MVC 애플리케이션을 위한 간단한 라우팅 시스템

프레임워크를 사용하고 싶지는 않지만 지저분한 URL과 복잡한 라우팅 시스템을 제거하고 싶습니까? 아마도 내가 당신을 위한 해결책을 가지고 있을 것입니다.



모든 PHP 프레임워크는 라우팅/디스패칭 시스템을 매우 잘 처리할 수 있지만 모든 사람이 자신의 작업을 수행하기 위한 프레임워크에 관심이 있는 것은 아닙니다. 나만의 라우팅 시스템을 만들고 싶어서 여기까지 오셨죠? 자, 이 문제에 대한 나의 접근 방식을 보여드리겠습니다. 이것이 적절한 라우팅 시스템을 수행하는 효과적인 방법을 얻는 데 도움이 될 것임을 누가 압니까?

함수 호출을 빌드하기 위해 잡아야 할 항목 이해



이 챌린지에서 당신을 도울 superglobal이 있습니다: $ _SERVER . PHP 문서에서 말하는 것처럼:

$_SERVER is an array containing information such as headers, paths, and script locations. The entries in this array are created by the web server. There is no guarantee that every web server will provide any of these; servers may omit some, or provide others not listed here.

https://www.php.net/manual/en/reserved.variables.server.php



PHP는 잠재적인 정보 부재에 대해 경고하지만, 우리가 사용할 기본 정보인 PATH_INFO가 제공될 가능성이 매우 높습니다. PATH_INFO가 없더라도(예: 실제 경로가 제공되지 않은 경우) 기본값(예: '/')을 만들 수 있습니다.

더 이상 소개하지 않고 코드를 작성해 봅시다!

**경고: 이 글을 쓰는 시점에서 저는 PHP 8.1을 사용하고 있습니다. 다음 코드는 PHP 버전뿐만 아니라 웹 서버에 따라 사용자 환경에서 작동하거나 작동하지 않을 수 있습니다.

1. URI 가져오기



첫 번째 단계는 단순히 URI를 가져와 여러 부분으로 나누는 함수를 만드는 것입니다. 이러한 부분은 컨트롤러, 메서드 및 메서드 매개 변수(args)를 나타냅니다.

private static function getURI() : array
{
    $path_info = $_SERVER['PATH_INFO'] ?? '/';
    return explode('/', $path_info);
}


따라서 다음과 같은 URL의 경우:

http://www.example.com/posts/view/3


당신은 얻을 것이다:

$uri[0] = 'posts';
$uri[1] = 'view';
$uri[2] = '3';


이제 post_id 인수로 받는 view라는 메서드가 있는 게시물 컨트롤러를 이미 상상할 수 있습니다.

2. getURI 정보 처리



컨트롤러, 메서드 및 인수(있는 경우)를 반환하는 개체를 빌드해 보겠습니다.

private static function processURI() : array
{
    $controllerPart = self::getURI()[0] ?? '';
    $methodPart = self::getURI()[1] ?? '';
    $numParts = count(self::getURI());
    $argsPart = [];
    for ($i = 2; $i < $numParts; $i++) {
        $argsPart[] = self::getURI()[$i] ?? '';
    }

    //Let's create some defaults if the parts are not set
    $controller = !empty($controllerPart) ?
        '\Controllers\\'.$controllerPart.'Controller' :
        '\Controllers\HomeController';

    $method = !empty($methodPart) ?
        $methodPart :
        'index';

    $args = !empty($argsPart) ?
        $argsPart :
        [];

    return [
        'controller' => $controller,
        'method' => $method,
        'args' => $args
    ];
}


getURI를 한 번 더 호출할 필요가 거의 없기 때문에 사용 사례를 단순화하고 동일한 함수에 모든 것을 작성할 수 있다는 점에 유의하세요. Single-responsibility principle (SRP) 목적으로 둘 다 분리하겠습니다.

여기에 사용된 컨트롤러는 컨트롤러라는 네임스페이스가 있는 예시일 뿐입니다. 일반적인 규칙은 Controllers\SomethingController이며 저는 이 방식을 좋아합니다.

3. 적절한 컨트롤러, 메서드 및 해당 인수를 마무리하고 호출하는 함수를 만듭니다.



URI를 분해하는 더러운 작업을 이미 수행했기 때문에 이것은 가장 재미있는 부분입니다.

public static function contentToRender() : void
{
    $uri = self::processURI();
    if (class_exists($uri['controller'])) {
        $controller = $uri['controller'];
        $method = $uri['method'];
        $args = $uri['args'];
        //Now, the magic
        $args ? $controller::{$method}(...$args) :
            $controller::{$method}();
    }
}


($args 배열 앞에 있는 세 개의 점을 array unpacking이라고 합니다. 기본적으로 PHP는 배열 값을 추출하여 별도의 변수로 인스턴스화합니다.)

하지만 기다려! 여기 뭔 일 있었 니?

이전 예제를 다시 선택하지만 추가 인수가 있습니다.

http://www.example.com/posts/view/3/excerpt


이 경우 다음이 있습니다.
  • 컨트롤러:\Controllers\PostsController
  • 메서드: 보기()
  • 인수: ('3', '발췌')

  • 따라서 호출은\Controllers\PostsController::view('3', 'excerpt')입니다. 기본적으로 ID가 '3'인 게시물의 발췌 부분입니다.

    매우 간단하면서도 효과적입니다. 메서드가 전달되지 않으면 색인을 기본 메서드로 가정합니다. 하지만 색인이라는 기본 메서드가 없고 명시적으로 지정하지 않으면 애플리케이션에서 오류가 발생한다는 점에 유의하세요.

    이것을 어떻게 부를 수 있습니까?

    Route::contentToRender();
    


    이것은 우리가 클래스를 Route라고 불렀다고 가정합니다. 또한 컨트롤러 메서드가 정적이라고 가정합니다. 정적이지 않은 경우 contentToRender() 함수를 약간 변경해야 합니다.

    대신에:

    $args ? $controller::{$method}(...$args) :
                $controller::{$method}();
    


    다음을 작성해야 합니다.

    $args ? (new $controller)->{$method}(...$args) :
            (new $controller)->{$method}();
    


    정적 메서드를 많이 사용하며 테스트하기가 더 어렵다는 것을 알고 있습니다. Karma는 그렇게 하면 조만간 나를 죽일 것입니다.

    결론적으로, 다음은 Route.php 클래스입니다.

    <?php
    
    class Route
    {
        public static function contentToRender() : void
        {
            $uri = self::processURI();
            if (class_exists($uri['controller'])) {
                $controller = $uri['controller'];
                $method = $uri['method'];
                $args = $uri['args'];
                //Now, the magic
                $args ? $controller::{$method}(...$args) :
                    $controller::{$method}();
            }
        }
    
        private static function getURI() : array
        {
            $path_info = $_SERVER['PATH_INFO'] ?? '/';
            return explode('/', $path_info);
        }
    
        private static function processURI() : array
        {
            $controllerPart = self::getURI()[0] ?? '';
            $methodPart = self::getURI()[1] ?? '';
            $numParts = count(self::getURI());
            $argsPart = [];
            for ($i = 2; $i < $numParts; $i++) {
                $argsPart[] = self::getURI()[$i] ?? '';
            }
    
            //Let's create some defaults if the parts are not set
            $controller = !empty($controllerPart) ?
            '\Controllers\\'.$controllerPart.'Controller' :
            '\Controllers\HomeController';
    
            $method = !empty($methodPart) ?
                $methodPart :
                'index';
    
            $args = !empty($argsPart) ?
                $argsPart :
                [];
    
            return [
                'controller' => $controller,
                'method' => $method,
                'args' => $args
            ];
        }
    }
    


    이것을 만드는 다른 방법이 있습니까?



    나는 당신이 그렇게 확신합니다! 나는 항상 더 깨끗하고 짧은 코드를 사용하므로 이 접근 방식을 개선하기 위한 제안이 있으면 의견에 알려주십시오. 그렇게 하면 PHP 팬보이인 우리는 PHP의 우수성 기준을 훨씬 더 높일 수 있습니다!

    좋은 하루 되세요!

    좋은 웹페이지 즐겨찾기