Маршрутизация

Примечание: Хотите узнать больше о маршрутизации? Посмотрите страницу "почему фреймворк?" для более подробного объяснения.

Основная маршрутизация в Flight выполняется путем сопоставления шаблона URL с функцией обратного вызова или массивом класса и метода.

Flight::route('/', function(){
    echo 'привет, мир!';
});

Маршруты сопоставляются в том порядке, в котором они определены. Первый сопоставленный маршрут будет вызван.

Обратные вызовы/Функции

Обратный вызов может быть любым объектом, который можно вызывать. Так что вы можете использовать обычную функцию:

function hello() {
    echo 'привет, мир!';
}

Flight::route('/', 'hello');

Классы

Вы также можете использовать статический метод класса:

class Greeting {
    public static function hello() {
        echo 'привет, мир!';
    }
}

Flight::route('/', [ 'Greeting','hello' ]);

Или создав объект сначала, а затем вызвать метод:


// Greeting.php
class Greeting
{
    public function __construct() {
        $this->name = 'Иванов Иван';
    }

    public function hello() {
        echo "Привет, {$this->name}!";
    }
}

// index.php
$greeting = new Greeting();

Flight::route('/', [ $greeting, 'hello' ]);
// Вы также можете сделать это без предварительного создания объекта
// Примечание: В конструктор не будут переданы аргументы
Flight::route('/', [ 'Greeting', 'hello' ]);
// Кроме того, вы можете использовать эту более короткую синтаксис
Flight::route('/', 'Greeting->hello');
// или
Flight::route('/', Greeting::class.'->hello');

Внедрение зависимостей через DIC (контейнер внедрения зависимостей)

Если вы хотите использовать внедрение зависимостей через контейнер (PSR-11, PHP-DI, Dice и т. д.), единственный тип маршрутов, где это доступно - это либо непосредственное создание объекта самостоятельно и использование контейнера для создания вашего объекта, или вы можете использовать строки для определения класса и метода для вызова. Загляните на страницу [Внедрение зависимостей] (/learn/extending) для более подробной информации.

Вот быстрый пример:


use flight\database\PdoWrapper;

// Greeting.php
class Greeting
{
    protected PdoWrapper $pdoWrapper;
    public function __construct(PdoWrapper $pdoWrapper) {
        $this->pdoWrapper = $pdoWrapper;
    }

    public function hello(int $id) {
        // делаем что-то с $this->pdoWrapper
        $name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
        echo "Привет, мир! Меня зовут {$name}!";
    }
}

// index.php

// Настройте контейнер в соответствии с вашими потребностями
// Смотрите страницу Внедрение зависимостей для более подробной информации о PSR-11
$dice = new \Dice\Dice();

// Не забудьте заново присвоить переменную '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
    'shared' => true,
    'constructParams' => [ 
        'mysql:host=localhost;dbname=test', 
        'root',
        'password'
    ]
]);

// Зарегистрируйте обработчик контейнера
Flight::registerContainerHandler(function($class, $params) use ($dice) {
    return $dice->create($class, $params);
});

// Маршруты как обычно
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// или
Flight::route('/hello/@id', 'Greeting->hello');
// или
Flight::route('/hello/@id', 'Greeting::hello');

Flight::start();

Маршрутизация методов

По умолчанию шаблоны маршрутов сопоставляются с каждым методом запроса. Вы можете отвечать на конкретные методы, поместив идентификатор перед URL.

Flight::route('GET /', function () {
  echo 'Получен запрос GET.';
});

Flight::route('POST /', function () {
  echo 'Получен запрос POST.';
});

// Невозможно использовать Flight::get() для маршрутов, так как это метод 
//    для получения переменных, а не для создания маршрута.
// Flight::post('/', function() { /* код */ });
// Flight::patch('/', function() { /* код */ });
// Flight::put('/', function() { /* код */ });
// Flight::delete('/', function() { /* код */ });

Вы также можете сопоставить несколько методов с одним обратным вызовом, используя разделитель |:

Flight::route('GET|POST /', function () {
  echo 'Получен запрос GET или POST.';
});

Кроме того, можно получить объект Router, который содержит некоторые вспомогательные методы для использования:


$router = Flight::router();

// картировка всех методов
$router->map('/', function() {
    echo 'привет, мир!';
});

// GET запрос
$router->get('/users', function() {
    echo 'пользователи';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();

Регулярные выражения

Вы можете использовать регулярные выражения в ваших маршрутах:

Flight::route('/user/[0-9]+', function () {
  // Это соответствует /user/1234
});

Хотя это метод доступен, рекомендуется использовать именованные параметры или именованные параметры с регулярными выражениями, так как они более читаемы и легче поддерживать.

Именованные параметры

Вы можете указать именованные параметры в ваших маршрутах, которые будут переданы в вашу функцию обратного вызова.

Flight::route('/@name/@id', function (string $name, string $id) {
  echo "привет, $name ($id)!";
});

Вы также можете включать регулярные выражения с именованными параметрами, используя разделитель ::

Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
  // Это соответствует /bob/123
  // Но не соответствует /bob/12345
});

Примечание: Сопоставление групп regex () с именованными параметрами не поддерживается. :'(

Опциональные параметры

Вы можете указать именованные параметры, которые являются необязательными для сопоставления путем обертывания сегментов в скобки.

Flight::route(
  '/blog(/@year(/@month(/@day)))',
  function(?string $year, ?string $month, ?string $day) {
    // Это соответствует следующим URL:
    // /blog/2012/12/10
    // /blog/2012/12
    // /blog/2012
    // /blog
  }
);

Любые несопоставленные необязательные параметры будут переданы как NULL.

Маски

Сопоставление выполняется только на отдельных сегментах URL. Если вы хотите сопоставить несколько сегментов, вы можете использовать спецсимвол *.

Flight::route('/blog/*', function () {
  // Это будет соответствовать /blog/2000/02/01
});

Для направления всех запросов на один обратный вызов, вы можете сделать следующее:

Flight::route('*', function () {
  // Что-то делаем
});

Передача

Вы можете передать выполнение следующему сопоставленному маршруту, вернув true из вашей функции обратного вызова.

Flight::route('/user/@name', function (string $name) {
  // Проверяем некоторое условие
  if ($name !== "Боб") {
    // Продолжить к следующему маршруту
    return true;
  }
});

Flight::route('/user/*', function () {
  // Это будет вызвано
});

Псевдоним маршрута

Вы можете назначить псевдоним маршруту, так чтобы URL можно было динамически генерировать позже в вашем коде (например, в шаблоне).

Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');

// позже в коде
Flight::getUrl('user_view', [ 'id' => 5 ]); // вернет '/users/5'

Это особенно полезно, если ваш URL случайно изменится. В предыдущем примере предположим, что пользователи были перемещены в /admin/users/@id вместо этого. С использованием псевдонима вам не нужно изменять места, где вы ссылаетесь на псевдоним, потому что псевдоним теперь вернет /admin/users/5, как в приведенном выше примере.

Псевдоним маршрутизации по-прежнему работает в группах также:

Flight::group('/users', function() {
    Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
});

// позже в коде
Flight::getUrl('user_view', [ 'id' => 5 ]); // вернет '/users/5'

Информация о маршруте

Если вы хотите проверить информацию о сопоставленном маршруте, вы можете запросить объект маршрута, передав true в качестве третьего параметра в метод маршрута. Объект маршрута всегда будет последним параметром, переданным в вашу функцию обратного вызова.

Flight::route('/', function(\flight\net\Route $route) {
  // Массив сопоставленных методов HTTP
  $route->methods;

  // Массив именованных параметров
  $route->params;

  // Соответствующее регулярное выражение
  $route->regex;

  // Содержит содержимое любого '*', используемого в шаблоне URL
  $route->splat;

  // Показывает путь URL.... если вам действительно нужно
  $route->pattern;

  // Показывает, какое промежуточное ПО назначено этому
  $route->middleware;

  // Показывает псевдоним, назначенный этому маршруту
  $route->alias;
}, true);

Группировка маршрутов

Иногда вам может потребоваться группировать связанные маршруты вместе (например, /api/v1). Вы можете сделать это, используя метод group:

Flight::group('/api/v1', function () {
  Flight::route('/users', function () {
    // Соответствует /api/v1/users
  });

  Flight::route('/posts', function () {
    // Соответствует /api/v1/posts
  });
});

Вы можете даже вкладывать группы из групп:

Flight::group('/api', function () {
  Flight::group('/v1', function () {
    // Flight::get() получает переменные, он не устанавливает маршрут! Смотрите ниже контекст объекта
    Flight::route('GET /users', function () {
      // Соответствует GET /api/v1/users
    });

    Flight::post('/posts', function () {
      // Соответствует POST /api/v1/posts
    });

    Flight::put('/posts/1', function () {
      // Соответствует PUT /api/v1/posts
    });
  });
  Flight::group('/v2', function () {

    // Flight::get() получает переменные, он не устанавливает маршрут! Смотрите ниже контекст объекта
    Flight::route('GET /users', function () {
      // Соответствует GET /api/v2/users
    });
  });
});

Группировка с объектным контекстом

Вы все еще можете использовать группировку маршрутов с объектом Engine следующим образом:

$app = new \flight\Engine();
$app->group('/api/v1', function (Router $router) {

  // используйте переменную $router
  $router->get('/users', function () {
    // Соответствует GET /api/v1/users
  });

  $router->post('/posts', function () {
    // Соответствует POST /api/v1/posts
  });
});

Потоковая передача

Теперь вы можете потоково отправлять ответы клиенту, используя метод streamWithHeaders(). Это полезно для отправки больших файлов, длительных процессов или генерации больших ответов. Потоковая передача маршрута обрабатывается немного по-другому, чем обычный маршрут.

Примечание: Передача ответов доступна только если у вас установлен параметр flight.v2.output_buffering в значение false.

Поток с ручными заголовками

Вы можете передавать ответ клиенту, используя метод stream() на маршруте. Если вы делаете так, вам необходимо установить все методы вручную перед тем, как что-то выводить в клиент. Это делается с помощью функции header() php или метода Flight::response()->setRealHeader().

Flight::route('/@filename', function($filename) {

    // очевидно, вы также будете очищать путь и прочее.
    $fileNameSafe = basename($filename);

    // Если у вас есть дополнительные заголовки для установки после выполнения маршрута
    // вы должны определить их, прежде чем что-то будет выведено.
    // Все они должны быть прямым вызовом функции header() или 
    // вызовом метода Flight::response()->setRealHeader()
    header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
    // или
    Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$fileNameSafe.'"');

    $fileData = file_get_contents('/some/path/to/files/'.$fileNameSafe);

    // Обработка ошибок и прочее
    if(empty($fileData)) {
        Flight::halt(404, 'Файл не найден');
    }

    // вручную установить длину контента, если хотите
    header# Маршрутизация

> **Примечание:** Хотите узнать больше о маршрутизации? Посмотрите страницу ["почему фреймворк?"](/learn/why-frameworks) для более подробного объяснения.

Основная маршрутизация в Flight выполняется путем сопоставления шаблона URL с функцией обратного вызова или массивом класса и метода.

```php
Flight::route('/', function(){
    echo 'привет, мир!';
});

Маршруты сопоставляются в том порядке, в котором они определены. Первый сопоставленный маршрут будет вызван.

Обратные вызовы/Функции

Обратный вызов может быть любым объектом, который можно вызывать. Так что вы можете использовать обычную функцию:

function hello() {
    echo 'привет, мир!';
}

Flight::route('/', 'hello');

Классы

Вы также можете использовать статический метод класса:

class Greeting {
    public static function hello() {
        echo 'привет, мир!';
    }
}

Flight::route('/', [ 'Greeting','hello' ]);

Или создав объект сначала, а затем вызвать метод:


// Greeting.php
class Greeting
{
    public function __construct() {
        $this->name = 'Иванов Иван';
    }

    public function hello() {
        echo "Привет, {$this->name}!";
    }
}

// index.php
$greeting = new Greeting();

Flight::route('/', [ $greeting, 'hello' ]);
// Вы также можете сделать это без предварительного создания объекта
// Примечание: В конструктор не будут переданы аргументы
Flight::route('/', [ 'Greeting', 'hello' ]);
// Кроме того, вы можете использовать эту более короткую синтаксис
Flight::route('/', 'Greeting->hello');
// или
Flight::route('/', Greeting::class.'->hello');