В этом разделе (метод direct
)
explode('@', $this->routes[$requestType][$uri])
Это должно быть
explode('@', $this->routes[$requestType][$regex])
Или просто (и предпочтительно):
explode('@', $controller)
какURI (для третьего) выглядит примерно так:
users/10
users/20
И фактический ключ: users/:id
, который также является значением $regex
(очевидно)
Код (Только для тестирования):
$routes = [
'GET' => [
'users'=>'UsersController@index',
'users/about'=>'UsersController@test',
'users/:id'=>'UsersController@show'
],
'POST' => []
];
$requestType = 'GET';
$uri = 'users/10';
foreach ($routes[$requestType] as $regex => $controller) {
$pattern = "@^" . preg_replace('/\\\:[a-zA-Z0-9\_\-]+/', '([a-zA-Z0-9\-\_]+)', preg_quote($regex)) . "$@D";
if ( preg_match($pattern, $uri, $matches ) ) {
print_r($matches[0]);
echo "\n";
print_r($routes[$requestType][$uri]);
echo "\n";
print_r($routes[$requestType][$regex]);
}
}
Вывод:
#$matches[0]
users/10
#with $uri as the key - $routes[$requestType][$uri]
<b>Notice</b>: Undefined index: users/10 in <b>[...][...]</b> on line <b>27</b><br />
#with $regex as the key - $routes[$requestType][$regex]
UsersController@show
Песочница
Также я думаю, что первый и второй должны работать, толькотот, с действительным регулярным выражением в качестве ключа, будет затронут из-за его «динамического» характера.
Другие вещи
Одна вещь, которую вам не хватает, это аргументы из URL, возьмите третий пример (users/10
), как вы передаете этот идентификатор (10
) в свой контроллер?Кроме того, если бы это был я, я бы нарушил зависимость, которая у вас есть в этой строке $controller = "App\\Controllers\\{$controller}";
, так как она ограничивает вас использованием только классов пространства имен App\\Controllers\...
.
Итак, чтобы исправить это, измените структуру данных наудалите этот знак @
.Поэтому вместо этого:
$router->get('users', 'UsersController@index');
Сделайте это следующим образом:
#Obj::class returns the fully qualified class name (includes namespace)
# PHP 5.6+ I think?
$router->get('users', [UsersController::class,'index']);
Что на самом деле упростит ваш код и даст вам возможность делать такие вещи (проще и гибче):
$router->get('users', function(){
//do something simple
});
#or
$router->get('users', 'somefunction');
#or (drop in plugins outside of your normal controller folder)
$router->get('users', 'Plugins/Users/Controllers/User);
Таким образом, мы должны внести небольшие изменения:
public function direct($uri, $requestType)
{
$matches = [];
foreach ($this->routes[$requestType] as $regex => $controller) {
$pattern = "@^" . preg_replace('/\\\:[a-zA-Z0-9\_\-]+/', '([a-zA-Z0-9\-\_]+)', preg_quote($regex)) . "$@D";
if ( preg_match($pattern, $uri, $matches ) ) {
//Simplify the code here and also pass the uri as an array
return $this->callAction($controller, explode('/', $uri));
}
}
throw new Exception('No route defined for this URI.');
}
protected function callAction($controller, array $args=[])
{
//you can check types here but all callables work with call_user_func & call_user_func_array
//you may be able to just check !is_callable($controller) for them all if you don't need the granularity
if(is_array($controller)){
//[object, method]
//[class name, method]
if(!class_exists($controller[0]) || !method_exists($controller[0], $controller[1])){
//the router has a direct interface to the end user
//because of this it must handle requests to bad URLs and such
//direct to 404 page, for example something like this
//you can and should "log" the errors, but don't show them
// ---- return $this->error404();
}
}else if(is_object($controller) && !is_callable($controller)){
//closure or magic method __invoke
// ---- return $this->error404();
}else if( !function_exists($controller) ){
//standard functions
// ---- return $this->error404();
}
return call_user_func_array($action, $args);
}
При этой простой настройке передаются все аргументы, включая имя контроллера, если он является частью URL.Например, использование третьего маршрута со значением этого users/10
вызовет
$UsersController->show('users', '10');
Может быть сложно удалить это, не вставляя «метод» в путь маршрута: например,
$router->get('users/about', 'UsersController@test');
Нет способа «узнать», важны ли «пользователи» для метода «теста».Теперь, если они совпадают:
$router->get('test/about', 'UsersController@test');
Вы можете удалить его.Обычно я видел этот шаблон в URL-адресах
www.yoursite.com/controller/method/...args
, который дает нам своего рода "грантополучатель" в отношении того, какие части.Но это ваш код, вы можете просто решить, что можете отказаться от первого, несмотря ни на что ...
Я должен отметить, что я не тестировал ни один из приведенных выше кодов, но, исходя из моего опыта, эти функции вывероятно, захочет в какой-то момент.
Ура!