SVNからのミラー
This commit is contained in:
163
src/Site/Lib/Route.php
Normal file
163
src/Site/Lib/Route.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
namespace Site\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'] ?? []);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user