Вот оптимизированная версия вашего кода. Основная идея - разделить компиляцию (определение что вызывать) и инстанциирование (создание объектов):
private static function compileHandler($handler, bool $is_middleware = false): array|string|\Closure
{
if (empty($handler)) {
return [];
}
// closure
if ($handler instanceof \Closure) {
return $handler;
}
// [ \Path\To\Class:class, "method" ]
if (is_array($handler)) {
if (count($handler) == 2) {
list($class, $method) = $handler;
} else {
$class = $handler[0];
$method = '__invoke';
}
self::checkClassExists($class);
self::checkMethodExists($class, $method);
// Возвращаем информацию о классе и методе для дальнейшего инстанциирования
return [
'type' => 'class_method',
'class' => $class,
'method' => $method,
'static' => false // будет определено при инстанциировании
];
}
// isString
if (is_string($handler) && str_contains($handler, '@')) {
// 'Class@method'
list($class, $method) = Helper::explode($handler, [null, '__invoke'], '@');
self::checkClassExists($class);
self::checkMethodExists($class, $method);
// Определяем статичность метода без создания экземпляра класса
try {
$reflection = new \ReflectionClass($class);
$reflected_method = $reflection->getMethod($method);
$isStatic = $reflected_method->isStatic();
return [
'type' => 'class_method',
'class' => $class,
'method' => $method,
'static' => $isStatic
];
} catch (\ReflectionException $e) {
self::$logger->error("Method '{$method}' not defined at '{$class}'", [self::$uri, self::$httpMethod, $class]);
throw new AppRouterHandlerError("Method '{$method}' not defined at '{$class}'", 500, [
'request' => self::$httpMethod . ' ' . self::$uri,
'uri' => self::$uri,
'method' => self::$httpMethod,
'info' => self::$routeInfo,
'rule' => self::$routeRule
]);
}
}
// остался вариант "функция"
self::checkFunctionExists($handler);
return $handler;
}
// Новый метод для инстанциирования и вызова handler'а
private static function invokeHandler($compiledHandler, array $parameters = [])
{
if (empty($compiledHandler)) {
if (!self::$option_allow_empty_handlers) {
throw new AppRouterHandlerError("Handler not found or empty", 500, [
'request' => self::$httpMethod . ' ' . self::$uri,
'uri' => self::$uri,
'method' => self::$httpMethod,
'info' => self::$routeInfo,
'rule' => self::$routeRule,
]);
}
return;
}
$actor = null;
// Если handler уже готов к вызову (замыкание или функция)
if ($compiledHandler instanceof \Closure || is_string($compiledHandler)) {
$actor = $compiledHandler;
}
// Если это информация о методе класса
elseif (is_array($compiledHandler) && isset($compiledHandler['type'])) {
if ($compiledHandler['type'] === 'class_method') {
$class = $compiledHandler['class'];
$method = $compiledHandler['method'];
if ($compiledHandler['static']) {
// Статический метод
$actor = [$class, $method];
} else {
// Нестатический метод - создаем экземпляр
if (array_key_exists($class, self::$instances)) {
$instance = self::$instances[$class];
} else {
// @todo: тут надо бы передавать параметры в конструктор
$instance = new $class();
// Кешируем инстанс, если нужно
if (self::$option_cache_instances) {
self::$instances[$class] = $instance;
}
}
$actor = [$instance, $method];
}
}
}
if ($actor) {
call_user_func_array($actor, $parameters);
}
}
// Компилируем handler (без инстанциирования)
$compiledHandler = self::compileHandler($handler, false);
// Инстанциируем и вызываем в нужном месте
self::invokeHandler($compiledHandler, $method_parameters);
Вот оптимизированная версия вашего кода. Основная идея - разделить компиляцию (определение что вызывать) и инстанциирование (создание объектов):
Использование:
Ключевые улучшения:
Разделение ответственности:
compileHandler()только анализирует handler и возвращает структурированные данныеinvokeHandler()занимается созданием объектов и вызовомОтложенное инстанциирование:
Гибкость:
Производительность:
self::$option_cache_instancesЧистота кода: