163 lines
4.5 KiB
PHP
163 lines
4.5 KiB
PHP
<?php
|
||
namespace Std\Lib;
|
||
|
||
class Route {
|
||
protected static array $routes = [];
|
||
protected static array $fallback = [];
|
||
|
||
/**
|
||
* ルート設定を言語固有のハンドラで初期化する
|
||
*
|
||
* @param array $routes ルート設定の配列
|
||
* @return void
|
||
*/
|
||
public static function init(array $routes): void {
|
||
self::$routes = $routes;
|
||
}
|
||
|
||
/**
|
||
* ルートを追加する
|
||
*
|
||
* @param string $method HTTPメソッド
|
||
* @param string $path URLパス
|
||
* @param string|callable $class ハンドラクラスとメソッド、またはコールバック
|
||
* @param array $params オプションのパラメータ
|
||
* @return array ルート設定
|
||
*/
|
||
public static function add(string $method, string $path, string|callable $class,
|
||
array $params = []): array {
|
||
$route = [
|
||
'method' => $method,
|
||
'path' => $path,
|
||
'class' => $class,
|
||
'params' => $params,
|
||
];
|
||
|
||
self::$routes[] = $route;
|
||
return $route;
|
||
}
|
||
|
||
/**
|
||
* 404処理用のフォールバックルートを設定する
|
||
*
|
||
* @param array|string|callable $class
|
||
* @return void
|
||
*/
|
||
public static function setFallback(array|string|callable $class): void {
|
||
self::$fallback = [
|
||
'class' => $class,
|
||
'params' => [],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 適切なルートをマッチさせて実行する
|
||
*
|
||
* @param string $uri リクエストURI
|
||
* @return void
|
||
*/
|
||
public static function dispatch(string $uri): void {
|
||
// URIをパスとクエリ文字列に分割
|
||
$uriParts = explode('?', $uri, 2);
|
||
$path = trim($uriParts[0], " \t\n\r\0\x0B/");
|
||
|
||
// ルートパスの処理(/?page=2のようなクエリパラメータを含む場合も処理)
|
||
if ($path === '') {
|
||
self::executeClass([
|
||
'class' => [new \Site\Controller\Home(), 'show'],
|
||
'params' => ['lang' => 'ja'],
|
||
]);
|
||
return;
|
||
}
|
||
|
||
if ($path === 'en') {
|
||
self::executeClass([
|
||
'class' => [new \Site\Controller\Home(), 'show'],
|
||
'params' => ['lang' => 'en'],
|
||
]);
|
||
return;
|
||
}
|
||
|
||
// パスに対してルートをマッチングする
|
||
foreach (self::$routes as $route) {
|
||
$matches = [];
|
||
|
||
if (self::matchRoute($route['path'], $path, $matches)) {
|
||
$params = self::extractParams($route['path'], $path);
|
||
$params = array_merge($route['params'], $params);
|
||
|
||
if (is_string($route['class'])) {
|
||
[ $class, $method ] = explode('@', $route['class']);
|
||
$controller = new $class();
|
||
self::executeClass([
|
||
'class' => [ $controller, $method ],
|
||
'params' => $params,
|
||
]);
|
||
|
||
return;
|
||
} elseif (is_callable($route['class'])) {
|
||
self::executeClass([
|
||
'class' => $route['class'],
|
||
'params' => $params,
|
||
]);
|
||
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// マッチするルートがない場合、フォールバックを実行
|
||
self::executeClass(self::$fallback);
|
||
}
|
||
|
||
/**
|
||
* ルートパターンとパスをマッチングする
|
||
*
|
||
* @param string $pattern ルートパターン
|
||
* @param string $path 現在のパス
|
||
* @param array $matches マッチを格納する参照
|
||
* @return bool
|
||
*/
|
||
protected static function matchRoute(string $pattern, string $path,
|
||
array &$matches = []): bool {
|
||
// ルートパターンを正規表現パターンに変換
|
||
$pattern = preg_replace('/\{([^:}]+)(?::([^}]+))?\}/', '(?P<$1>[^/]+)', $pattern);
|
||
$pattern = str_replace('/', '\/', $pattern);
|
||
return (bool)preg_match('/^'.$pattern.'$/', $path, $matches);
|
||
}
|
||
|
||
/**
|
||
* パターンに基づいてパスから名前付きパラメータを抽出する
|
||
*
|
||
* @param string $pattern ルートパターン
|
||
* @param string $path 現在のパス
|
||
* @return array
|
||
*/
|
||
protected static function extractParams(string $pattern, string $path): array {
|
||
$params = [];
|
||
$patternParts = explode('/', $pattern);
|
||
$pathParts = explode('/', $path);
|
||
|
||
foreach ($patternParts as $k => $v) {
|
||
if (preg_match('/\{([^:}]+)(?::([^}]+))?\}/', $v, $matches)) {
|
||
if (isset($pathParts[$k])) {
|
||
$params[$matches[1]] = $pathParts[$k];
|
||
}
|
||
}
|
||
}
|
||
|
||
return $params;
|
||
}
|
||
|
||
/**
|
||
* ルートクラスを実行する
|
||
*
|
||
* @param array $route ルート設定
|
||
* @return void
|
||
*/
|
||
protected static function executeClass(array $route): void {
|
||
if (is_callable($route['class'])) {
|
||
call_user_func($route['class'], $route['params'] ?? []);
|
||
}
|
||
}
|
||
} |