PHP で FastRoute を使って API のモックを作っていていました。
cf. GitHub - nikic/FastRoute: Fast request router for PHP
<?php require 'vendor/autoload.php'; $dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) { $r->addRoute('GET', '/api', 'handler'); }); // Fetch method and URI from somewhere $httpMethod = $_SERVER['REQUEST_METHOD']; $uri = $_SERVER['REQUEST_URI']; // Strip query string (?foo=bar) and decode 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: echo '404 Not Found'; break; case FastRoute\Dispatcher::METHOD_NOT_ALLOWED: $allowedMethods = $routeInfo[1]; echo '405 Method Not Allowed'; break; case FastRoute\Dispatcher::FOUND: $handler = $routeInfo[1]; $vars = $routeInfo[2]; echo $handler($vars); break; }
addRoute($method, $uri, $handler)
という形でルーティングの設定をして、最後の switch文の FastRoute\Dispatcher::FOUND:
内で $handler
が呼び出されるという感じです。
$handler をネームスペース付きのクラスのスタティックメソッドにしていて $handler の指定にハマってしまったのでメモ
文字列にしないとクラス変数を参照しようとしてしまう
()
を付けるとそのまま呼び出されてしまうから、スタティックメソッド名を指定しようとしたら、クラス変数を探してしまってそんな値はないというエラーになってしまいました。
<?php require_once './routes/user.php'; use MyApp\Route\User; $dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) { $r->addRoute('GET', '/api/user/{id:\d+}', User::get_user); // -> get_user というクラス変数を参照するのでエラー });
=> Fatal error: Uncaught Error: Undefined class constant 'get_user'
これは、まぁそうだよね。って感じ
$handler を文字列で指定する時、use で namespace を指定してもダメ
次に文字列で指定しようとした際に use
をしているからよ次のようにしてみた所またしても エラー になってしまいました。
<?php require_once './routes/user.php'; use MyApp\Route\User; $dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) { $r->addRoute('GET', '/api/user/{id:\d+}', 'User::get_user'); });
=> Fatal error: Uncaught Error: Class 'User' not found in
Class User そのものが見つからない、つまり名前空間 (namespace) が解決されていのが原因っぽい
文字列をメソッドとして使用する、つまり可変関数の時は namsespace をフルで指定する必要がある
次に $hander の部分に namespace をフルで含めた指定にすると意図したとおりに動作しました。
<?php require_once './routes/user.php'; $dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) { $r->addRoute('GET', '/api/user/{id:\d+}', 'MyApp\Route\User::get_user'); // -> OK });
これはつまり $handler は文字列を関数として呼び出しているので可変関数で、PHPの可変関数は名前空間 (namespace) をフルで指定する必要があるということのようです。
名前空間と動的言語機能
完全修飾名 (クラス名に名前空間プレフィックスをつけたもの) を使う必要があります。 動的なクラス名、関数名あるいは定数名においては修飾名と完全修飾名に差はないので、 先頭のバックスラッシュはなくてもかまいません。
cf. PHP: 名前空間と動的言語機能 - Manual
所感
通ってるスクールが PHP って方針で超久々に PHP 触ってて、WordPress こテーマ作りまくってた頃書いてたはずなのに めちゃめちゃPHPのこと忘れてて、なんもわからん…ってなってました。
[参考]