Comment la réflexion dans Laravel fonctionne-t-elle réellement?
J'ai essayé de le déboguer pour voir comment Laravel utilise la réflexion dans le constructeur ou les méthodes d'un contrôleur pour résoudre leurs dépendances et sous-dépendances, puis nous la rendre.
Mais j'ai trouvé ça difficile, et c'est très compliqué de voir et même de comprendre 50% de. Sautant de classe en classe, je ne peux pas vraiment le voir. J'ai essayé plusieurs fois en le déboguant avec de faibles résultats de compréhension.
Je suis très impressionné par cela et par la réflexion, et la façon dont Laravel l'utilise fait brûler mon cœur — c'est tout simplement magnifique. Et je souhaite bien comprendre cela — tout le processus — en général, et pas à pas.
Commencer de frapper la route pour finalement avoir, disons, dd($x)
, où $x
Est d'un argument de méthode et est un TestClass
qui a une autre dépendance de TestClass2
qui devrait être construit par: $x = new TestClass(new TestClass2());
Je pense que ce sont de belles mécaniques et architectures, et comprendre c'est quelque chose que je veux tellement.
Encore une fois, ma question est: comment fonctionne la réflexion dans Laravel fonctionne réellement?
Il ne s'agit pas de dd
les gars ... Disons sans dd
. Comme je l'ai dit plus tôt - lorsque nous avons cet objet instancié à partir du class method
. Il ne s'agit pas de le vider, il s'agit simplement de l'avoir depuis method injection
Par reflection
.
dd
n'était qu'un exemple. Cela peut même être die(var_dump());
et ça marchera
Laravel utilise PHP réflexion API pour plusieurs composants. Parmi ceux-ci, l'inverseur de contrôle (IoC) conteneur d'injection de dépendance et controller injection de méthode sont les plus visibles pour les développeurs.
Pour illustrer plus clairement l'utilisation de la réflexion, voici une version considérablement simplifiée de la routine Laravel's classe de conteneur IoC utilise pour construire Les dépendances d'un objet par injection de constructeur:
function build($className)
{
$reflector = new ReflectionClass($className);
$constructor = $reflector->getConstructor();
foreach ($constructor->getParameters() as $dependency) {
$instances[] = build($dependency->getClass()->name);
}
return $reflector->newInstanceArgs($instances);
}
Comme nous pouvons le voir, le concept n'est pas trop difficile à comprendre. Le conteneur utilise PHP ReflectionClass
pour trouver les noms des classes dans le constructeur d'un objet, puis parcourt récursivement chacun de ces noms pour créer des instances de chaque objet dans l'arborescence des dépendances. Avec ces instances, build()
instancie enfin la classe d'origine et passe les dépendances en arguments au constructeur.
L'injection de méthode du contrôleur utilise la même fonctionnalité de conteneur montrée ci-dessus pour résoudre les instances de dépendances déclarées comme paramètres de méthode, mais il y a un peu de logique supplémentaire nécessaire pour séparer les dépendances de classe à partir des paramètres de l'itinéraire:
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);
}
Encore une fois, cette adaptation est allégée pour mettre en évidence le rôle que joue la réflexion et s'appuie sur notre fonction build()
présentée précédemment. La classe ControllerDispatcher
utilise la méthode getParameters()
de PHP ReflectionMethod
pour déterminer laquelle les paramètres attendus par une méthode de contrôleur, puis les parcourent pour trouver des paramètres qui représentent les dépendances qu’elle peut résoudre à partir du conteneur. Ensuite, il épissure chaque dépendance qu'il retrouve dans le tableau de paramètres de route qu'il renvoie à la méthode de contrôleur définie pour la route. Voir RouteDependencyResolverTrait
pour plus de détails.
Si nous ignorons le processus d'amorçage de l'application, cette cascade d'injection de dépendances démarre généralement pour une demande lorsque Laravel mappe une demande à une route, puis détermine à quel contrôleur transmettre la demande. Laravel résout d'abord une instance du contrôleur à partir du conteneur, qui crée toutes les dépendances injectées par le constructeur. Ensuite, Laravel trouve la méthode de contrôleur appropriée et résout toutes les dépendances pour le arguments selon les besoins.
Comme montré ici, Laravel utilise des techniques relativement simples pour implémenter ces outils en utilisant la réflexion. Cependant, contrairement aux exemples montrés dans cette réponse, le framework ajoute beaucoup de code supplémentaire pour les rendre aussi robustes et flexibles comme ils le sont aujourd'hui.