private $classConfig;
Использовать типизованные параметры
$this->classConfig = new Config;
Использовать DI и интерфейсы, ниже подробнее.
$arr = $this->classConfig->getConfigs('routes'); // Вывод роутеров (Ниже пример)
if ($arr) {
foreach ($arr as $key => $value) {
$route = '#^' . $key . '$#';
$this->routes[$route] = $value;
}
}
Плохо. Почитайте про GRASP паттерны, особенно Information expert и, на будущее - Coupling/Cohesion(совсем-совсем на будущее, хотя можете глянуть докладик -
тык, думаю сойдёт).
Без регулярных выражений можно обойтись.
Информация должна быть там где данные, не нужно доставать данные из какого-то объекта чтобы принять решение на основе их - попросите объект с данными принять решение.
Как вариант - вместо этого должен инжектиться (по интерфейсу) какой-нибудь ControllerInfoProvider, или RoutesProvider, с методом вроде
getControllerInfo($requestData);
, который вернёт экземпляр DTO вроде
/**
* @psalm-immutable
*/
class ControllerInfo {
public string $controllerClass;
public string $controllerMethod;
/**
* @var string[]
* @psalm-var class-string[]
*/
public array $beforeControllerMiddlewares;
/**
* @param string[] $beforeControllerMiddlewares
* @psalm-param class-string[] $beforeControllerMiddlewares
*/
public function __construct(string $controllerClass, string $controllerMethod, array $beforeControllerMiddlewares = [])
{
$this->controllerClass = $controllerClass;
$this->controllerMethod = $controllerMethod;
$this->beforeControllerMiddlewares = $beforeControllerMiddlewares;
}
}
Ну и ещё можно postControllerMiddlewares, или, с более понятным названием вроде ResposeAwareMiddlewares, потому что
миддлвари это хорошо.
Ещё там аннотации для статического анализатора
psalm, потому что ловить ошибки на этапе разработки запустив
./vendor/bin/psalm
лучше чем ловить их после запуска кода, особенно в продакшене.
Впрочем, на самом деле это чутка оверхед для вас, но когда-нибудь разобраться с этим всем стоит.
$url = trim($_SERVER['REQUEST_URI'], '/');
foreach ($this->routes as $route => $params) {
if (preg_match($route, $url, $matches)) {
$this->params = $params;
if (isset($this->params['folder'])) {
$this->params['folder'] = '\\' . ucfirst($this->params['folder']);
}
return true;
}
}
return false;
Не нужно в Роутере глобальные переменные парсить. Пусть до роутера это дело обработает какой-нибудь RequestParser, и создаст DTO-объект запроса, можно по
psr-7, можно без него, чтобы проще было. Это не критично.
По поводу параметров в контроллере - стоит обратить внимание на
argument resolvers, и не заниматься этим в роутере. Вещь отличная и удобная, свои аргумент резолверы тоже пишутся просто.
echo "<b>" . ucfirst($this->params['controller']) . "Controller</b> or <b>" . $action . "</b> not found.";
return false;
Не роутера это дело, в stdout писать. Пусть кинет исключение, а ответ пользователю сформируется уровнем выше.
$path = 'app\controllers' . $this->params['folder']
Роутер не должен определять структуру директорий. Используйте полное имя в конфиге роутов, т.е.
SomeController::class
.
Сваливать все контроллеры в одну папочку занятие сомнительное.
Почему (зачем) предпочитаю хранить роутеры в файлах (в базе данных), если можно автоматизировать
Как удобнее так и храните. Конфиг, база, аннотации, php массив - без разницы
Нужно ли корректировать код, указанный выше или и так сойдет?
Чем больше сделаете из описанного выше - тем лучше.
Что лучше: загружать контроллер из роутера или загружать класс контроллер (проверки...), который будет загружать необходимый контроллер?
Получили ControllerInfo, обратились в
ControllersRegistry->getControllerClass()
, получили инстанс контроллера.
Далее либо вызвали нужный метод, либо, если всё же сделали - попросили аргумент резолвер распарсить и инициализировать аргументы:
$args = $this->argumentsResolver->resolve($controller, $method, $request);
Вызвали миддлвари, что-то вроде этого:
https://github.com/thephpleague/tactician/blob/mas... , вызвали контроллер:
$response = $controller->{$action}(... $args)