• Как сделать простой php-роутер?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    /app
    /app/controllers
    /app/controllers/hello
    /app/controllers/hello/world.php
    /app/controllers/index.php
    /app/routes.php
    /public
    /public/index.php
    /.htaccess
    /app.php

    .htaccess
    # .htaccess
    
    Options +FollowSymlinks -Indexes
    DirectoryIndex public/index.html public/index.php
    
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-l
    RewriteRule ^(.*)$ /public/index.php?_url=/$1 [L,QSA]


    app.php
    <?php
    // app.php
    
    define('__ROOT__', __DIR__);
    define('__APP__', realpath(__ROOT__ . '/app'));
    
    $routes = require_once __APP__ . '/routes.php';
    
    foreach ($routes as $pattern => $action) {
      if (! preg_match($pattern, $_SERVER['REQUEST_URI'], $matches)) continue;
    
      $action = array_filter(explode('/', $action));
      $file = basename(array_pop($action), '.php');
    
      array_push($action, $file);
    
      require __APP__ . '/controllers/' . implode('/', $action) . '.php';
      break;
    }


    public/index.php
    <?php
    // public/index.php
    
    // strict
    register_shutdown_function(function () {
      $error = error_get_last();
      if (! $error) return;
      if (! ($error['line'] ?? null)) return;
    
      $errno    = $error['type'];
      $errmsg   = $error['message'];
      $errfile  = $error['file'];
      $errline  = $error['line'];
    
      handle(new \ErrorException($errmsg, $errcode = -1, $errno, $errfile, $errline));
    });
    
    // error
    set_error_handler(function ($errno, $errmsg, $errfile, $errline) {
      if (! $errline) return;
    
      throw new \ErrorException($errmsg, $errcode = -1, $errno, $errfile, $errline);
    }, E_ALL);
    
    // handle
    function handle(\Throwable $e)
    {
      echo '<pre>';
      die(var_dump($e));
    }
    
    // index.php
    try {
      // run application
      require_once __DIR__ . '/../app.php';
    
    } catch (\Throwable $e) { handle($e); }


    app/routes.php
    <?php
    // app/routes.php
    
    $routes = [];
    
    foreach ([
      '/' => 'index',
      '/foo/bar' => 'index',
      '/hello/world' => 'hello/world',
    ] as $pattern => $action) {
      $routes[ '/^' . preg_quote($pattern, '/') . '$/' ] = $action;
    }
    
    return $routes;


    app/controllers/index.php
    <?php
    // app/controllers/index.php
    
    echo 'Index';


    app/controllers/hello/world.php
    <?php
    // app/controllers/hello/world.php
    
    echo 'Hello/World';
    Ответ написан
    5 комментариев
  • Как сделать простой php-роутер?

    sergiks
    @sergiks Куратор тега PHP
    ♬♬
    На github есть много популярных (по числу звёзд) реализаций php router'а.

    Задача типичная, решений много.

    Для обучения так же полезно, как писать велосипед, изучать чужой код.
    Ответ написан
    Комментировать
  • Как сделать простой php-роутер?

    orlov0562
    @orlov0562 Куратор тега PHP
    I'm cool!
    .htaccess
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-l
    RewriteRule ^(.+)$ index.php?route=$1 [QSA,L]


    index.php
    try {
        // берем переданный роут
        $route = trim($_REQUEST['route']??'index');
    
        // проверяем, если в конце слеш, то это index роут
        if (substr($route,'-1') == '/') $route.='index';
    
        // минимальная защита от инклуда неожидаемых файлов
        // ограничиваем имена до символов a-b, 0-9, тире, нижнее подчеркивание и слеш
        if (!preg_match('~^[-a-z0-9/_]+$~i', $route)) throw new Exception('Not allowed route');
    
        // генерим путь к файлу
        $filePath = dirname(__FILE__).'/pages/'.$route.'.php';
    
        // если не существует выкидываем ошибку
        if (!file_exists($filePath)) throw new Exception('Route not found');
    
        // если существует, инклудим файл
        include $filePath;
    } catch (Throwable $ex) {
        // в случае любых ошибок, показываем 404
        // тут обычно делают разные типы эксепшенов и разделяют 400 и 500 ошибки
        include dirname(__FILE__).'/pages/404.php';
    }


    структура вызова
    / => /pages/index.php
    /index => /pages/index.php
    /about => /pages/about.php
    /user/ => /pages/user/index.php
    /user/index => /pages/user/index.php
    /user/hello => /pages/user/hello.php
    ...
    /404 => /pages/404.php
    /<not-found> => /pages/404.php
    /user/<not-found> => /pages/404.php

    Далее доработать по потребностям.

    Но, я бы не советовал самому пилить, т.к. есть много всяких нюансов. Кроме того, есть куча готовых, проверенных, решений. Разве что, просто для интереса.
    Ответ написан
    2 комментария