Ответ 1
Laravel использует PHP API отражения для нескольких компонентов. Из них контейнер инкрементов управления (IoC) и контроллер метод наиболее заметен для разработчиков.
Чтобы более четко проиллюстрировать использование рефлексии, здесь резко упрощена версия рутины Laravel класс контейнера IoC используется для создания объекта зависимостей через инъекцию конструктора:
function build($className)
{
$reflector = new ReflectionClass($className);
$constructor = $reflector->getConstructor();
foreach ($constructor->getParameters() as $dependency) {
$instances[] = build($dependency->getClass()->name);
}
return $reflector->newInstanceArgs($instances);
}
Как мы видим, понятие не так сложно понять. Контейнер использует PHP ReflectionClass
, чтобы найти имена классов в конструкторе объектов, а затем рекурсивно прокручивает каждое из этих имен для создания экземпляры каждого объекта в дереве зависимостей. С этими экземплярами build()
, наконец, создает экземпляр исходного класса и передает зависимости в качестве аргументов конструктору.
Впрыск метода контроллера использует те же функции контейнера, которые показаны выше, чтобы разрешать экземпляры зависимостей, объявленных как параметры метода, но есть немного дополнительной логики, необходимой для разделения зависимостей класса от параметров маршрута:
function dispatch(Route $route, Controller $controller, $methodName)
{
$routeParameters = $route->parametersWithoutNulls();
$method = new ReflectionMethod($controller, $methodName);
foreach ($method->getParameters() as $index => $parameter) {
$class = $parameter->getClass();
if ($class !== null) {
$instance = build($class->name);
array_splice($routeParameters, $index, 0, [ $instance ]);
}
}
$controller->callAction($methodName, $routeParameters);
}
Опять же, эта адаптация уменьшена, чтобы подчеркнуть роль воспроизведения отражений и опирается на нашу функцию build()
, показанную ранее. Класс ControllerDispatcher
использует getParameters()
метод PHP ReflectionMethod
, чтобы определить, какие параметры ожидает метод контроллера, а затем перебирает их, чтобы найти параметры, представляющие зависимости, которые он может решить из контейнера. Затем он объединяет каждую зависимость, которую он находит обратно в массив параметров маршрута, который он возвращает обратно к методу контроллера, определенному для маршрута. Подробнее см. RouteDependencyResolverTrait
.
Если мы проигнорируем процесс начальной загрузки приложения, этот каскад инъекции зависимостей обычно начинается для запроса, когда Laravel сопоставляет запрос маршруту и затем определяет, какой контроллер должен передать запрос. Сначала Laravel разрешает экземпляр контроллера из контейнера, который создает любые зависимые от конструктора зависимости. Затем Laravel находит соответствующий метод контроллера и разрешает любые зависимости для аргументов по мере необходимости.
Как показано здесь, Laravel использует относительно простые методы для реализации этих инструментов с использованием отражения. Однако, в отличие от примеров, показанных в этом ответе, структура добавляет много дополнительного кода, чтобы сделать их такими же надежными и гибкими, как сегодня.