PHP で FastRoute を使ったルーティング
RestfulなAPIを作りたいと思った時に、Laravelは大袈裟すぎると思いました。何か軽量なフレームワークがPHPでないかと探していたところいくつか候補がありました。FastRoute、Lumen、Slimなど。今回はFastRouteを使ってみることにしました。ちなみに、Laravelの軽量版がLumenだそうで、Lumenのルーター部分はFastRouteが使われているそうです。またSlimも内部的にFastRouteを使うことがあるようです。
開発環境
項目 | バージョン |
---|---|
macOS | 14.2 |
php | 8.3.2 |
composer | 2.6.6 |
nikic/FastRouteはcomposerでインストールします。
ライブラリ | バージョン | インストールコマンド |
---|---|---|
nikic/fast-route | 1.3 | composer require nikic/fast-route |
このライブラリは高速でシンプルなルーティングを提供し、大規模なフレームワークを必要としない小規模なプロジェクトやAPIに最適とのことです。
FastRouteの準備
ここからは、プロジェクトディレクトリの新規作成から、composerを使用してnikic/fast-routeを設定し、テスト用のサーバーを起動するまでの手順をご紹介します。
プロジェクトディレクトリの作成
ターミナルを開き、新規プロジェクト用のディレクトリを作成します。
mkdir my_project
cd my_project
Composerの初期化
プロジェクトディレクトリでComposerを初期化します。途中で質問が表示されますが、すべてリターンキーのデフォルトで問題ありません。これにより、composer.jsonファイルが作成されます。
composer init
nikic/fast-routeのインストール
次に、nikic/fast-routeライブラリをプロジェクトに追加します。
composer require nikic/fast-route
プログラミング
FastRouteのインストールが完了したところで、ここからはPHPで実際にプログラミングしていきます。
ルーティングの設定
プロジェクトディレクトリ内にindex.phpファイルを作成し、次のコードを記述します。
<?php
require 'vendor/autoload.php';
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
$r->addRoute('GET', '/user/{userId}/info', function ($args) {
echo "User ID: " . $args['userId'];
});
});
$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
if (false !== $pos = strpos($uri, '?')) {
$uri = substr($uri, 0, $pos);
}
$uri = rawurldecode($uri);
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
switch ($routeInfo[0]) {
case FastRoute\Dispatcher::NOT_FOUND:
http_response_code(404);
echo "404 Not Found";
break;
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
http_response_code(405);
echo "405 Method Not Allowed";
break;
case FastRoute\Dispatcher::FOUND:
$handler = $routeInfo[1];
$vars = $routeInfo[2];
call_user_func($handler, $vars);
break;
}
ビルトインサーバーの起動
PHPのビルトインサーバーを起動して、作成したAPIをテストします。以下のコマンドをプロジェクトディレクトリで実行します。
php -S localhost:8885
これで、http://localhost:8885/user/123/info などにアクセスすると、設定したルーティングに基づいて「User ID: 123」のようなレスポンスが得られます。
あっという間にAPIの作成ができてしまいまたね。Laravelのような巨大なフレームワークと違って、プロジェクトディレクトリもシンプルです。
プログラムの解説
先ほどのプログラムを解説します。プログラム中のcall_user_funcはPHPにおいて、コールバック関数を呼び出すための関数です。次のようにして関数名と引数を渡すことができます。
function myFunction($param) {
echo $param;
}
call_user_func('myFunction', 'Hello, World!');
先ほどのソースコードを見てみましょう。
case FastRoute\Dispatcher::FOUND:
$handler = $routeInfo[1];
$vars = $routeInfo[2];
call_user_func($handler, $vars);
break;
このコードでは、FastRouteルーターを使用してURLパターンに基づいてリクエストを処理しています。$routeInfo変数は、ディスパッチされたルートの情報を含む配列です。この配列の内容は、リクエストされたURLとHTTPメソッドに基づいて決定されます。
- $routeInfo[0]はディスパッチの結果を表します。
- $routeInfo[1]はルートハンドラです。このハンドラは、ルートが定義された際に指定されたコールバック関数です。ルーティングが成功し、FastRoute\Dispatcher::FOUNDが返された場合、このハンドラが呼び出されます。
- $routeInfo[2]はルートパラメータの連想配列です。これには、URLパターン内のプレースホルダーにマッチした値が格納されます。
以下の部分について詳しく説明します:
$handler = $routeInfo[1];
$vars = $routeInfo[2];
call_user_func($handler, $vars);
- $handlerには、ルートがマッチした際に実行されるコールバック関数が格納されます。この例では、/user/{userId}/infoパターンにマッチした場合の関数が入ります。
- $varsには、URLから抽出されたパラメータが格納されます。例えば、リクエストURLが/user/123/infoだった場合、$varsは['userId' => '123']のような連想配列になります。
- call_user_func($handler, $vars);は、抽出されたコールバック関数($handler)を、URLから抽出されたパラメータ($vars)を引数として呼び出します。
この処理により、リクエストURLに応じて動的に異なる関数を呼び出し、パラメータを渡すことができます。
call_user_func($handler, $vars);で呼ばれるコールバック関数は、$dispatcherの設定部分でaddRouteメソッドを使用してルートが定義された時に指定された関数です。この例では、/user/{userId}/infoのURLパターンに一致するGETリクエストに対して指定されたコールバック関数が該当します。具体的には、以下の部分で定義された無名関数(クロージャ)です:
$r->addRoute('GET', '/user/{userId}/info', function ($args) {
echo "User ID: " . $args['userId'];
});
この無名関数は、リクエストのURLが/user/{userId}/infoのパターンにマッチするときに、$routeInfo[1](つまり$handler)に格納されます。そして、call_user_funcを使用して呼び出される際には、$vars(この場合はURLから抽出されたuserIdの値を含む配列)が引数として渡されます。
したがって、リクエストURLが/user/123/infoだった場合、$varsは['userId' => '123']という連想配列になり、この無名関数が以下のように実行されます:
echo "User ID: 123";
このように、call_user_func($handler, $vars);は指定されたURLパターンに一致するリクエストが来た際に、対応するコールバック関数を動的に呼び出し、適切なパラメータ(この例ではユーザーID)を渡して処理を実行する役割を果たします。
まとめ
あっという間にFastRouteを使ってルーティング処理ができました。軽量でわかりやすく好印象です。ただし、ルーティングに必要最小限のライブラリなので、実際に運営するにあたってデータベースアクセスや、バリデーション、ロギング機能などを別途ライブラリを使って実装しなければなりません。 そうなるとLumenやSlimといったもう少し高機能なミドルウェアの選択も、考慮に入れたほうが良さそうです。