1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143:
<?php
namespace Teto\Routing;
final class CommonPrefixTrieRouter
{
const URL_PARAMETER_TYPE_NUM = '[';
const URL_PARAMETER_TYPE_STRING = ']';
private static $VALID_STATE_MARK = '>';
private static $URL_PARAMETER_NAME = 'name';
public static function search($trie, $request_uri, $http_method)
{
$p = $trie[$http_method];
$length = strlen($request_uri);
$i = 0;
$ok = (0 < $length);
$result = [];
while ($i < $length) {
if ($request_uri[$i] !== '/') {
$ok = false;
break;
}
$str = '' . $request_uri[$i++];
$num_only = true;
while ($i < $length && $request_uri[$i] !== '/') {
$str .= $request_uri[$i];
$x = ord($request_uri[$i]);
$num_only &= (48 <= $x && $x <= 57);
$i++;
}
if (isset($p[$str])) {
$p = $p[$str];
} elseif ($num_only && isset($p[self::URL_PARAMETER_TYPE_NUM])) {
$p = $p[self::URL_PARAMETER_TYPE_NUM];
$result[$p[self::$URL_PARAMETER_NAME]] = substr($str, 1);
} elseif (isset($p[self::URL_PARAMETER_TYPE_STRING])) {
$p = $p[self::URL_PARAMETER_TYPE_STRING];
$result[$p[self::$URL_PARAMETER_NAME]] = substr($str, 1);
} else {
$ok = false;
break;
}
}
return $ok && isset($p[self::$VALID_STATE_MARK]) ? ['value' => $p[self::$VALID_STATE_MARK], 'params' => $result]
: null;
}
public static function trieConstruction(array $conf)
{
$trie = [];
foreach ($conf as $con) {
$http_method = $con[0];
$path = $con[1];
$value = $con[2];
$param_mapping = isset($con[3]) ? $con[3] : [];
if (!isset($trie[$http_method])) {
$trie[$http_method] = [];
}
$node = &$trie[$http_method];
$path_length = strlen($path);
$i = 0;
while ($i < $path_length) {
if ($path[$i++] !== '/') {
throw new Exception(sprintf("不正なパスが設定されています %s", $path));
}
$partial_path = '/';
while ($i < $path_length && $path[$i] !== '/') {
$partial_path .= $path[$i++];
}
$is_url_parameter = false;
$url_param_name = null;
if (1 < strlen($partial_path) && $partial_path[1] === ':') {
$is_url_parameter = true;
$url_param_name = substr($partial_path, 2);
if (!isset($param_mapping[$url_param_name])) {
throw new Exception(sprintf("URLパラメータ :%s に対する設定が足りません", $url_param_name));
}
$partial_path = $param_mapping[$url_param_name];
}
if (!isset($node[$partial_path])) {
if ($is_url_parameter) {
$node[$partial_path] = [self::$URL_PARAMETER_NAME => $url_param_name];
} else {
$node[$partial_path] = [];
}
} else {
if ($is_url_parameter && $node[$partial_path][self::$URL_PARAMETER_NAME] !== $url_param_name) {
throw new Exception(sprintf("URLパラメータに別名をつけようとしています %s (:%s, :%s)", $path, $url_param_name, $node[$partial_path][self::$URL_PARAMETER_NAME]));
}
}
$node = &$node[$partial_path];
}
if (isset($node[self::$VALID_STATE_MARK])) {
throw new Exception(sprintf("重複したルーティングルールがあります %s", $path));
}
$node[self::$VALID_STATE_MARK] = $value;
}
return $trie;
}
}