Die PHP Reflection API ist eine eingebaute PHP-Funktionalität, um Metainformationen zu Klassen, Methoden, Properties, Funktionen und Parametern zur Laufzeit auszulesen und zu untersuchen. Sie können etwa herausfinden, welche Argumente ein Konstruktor erwartet oder welche Attribute an einer Methode hängen — ohne den Quellcode zu ändern.Laravel nutzt die Reflection API intern in Illuminate/Container/Container.php intensiv, um automatische Auflösung im DI-Container, das Auslesen von PHP-Attributen und Method-Injection zu realisieren.
$ref = new ReflectionClass(UserController::class);$ref->getName(); // Voll qualifizierter Klassenname$ref->getShortName(); // Nur Klassenname$ref->isInstantiable(); // Ist die Klasse instanziierbar?$ref->getConstructor(); // Konstruktor als ReflectionMethod$ref->getMethods(); // Alle Methoden als ReflectionMethod[]$ref->getProperties(); // Alle Properties als ReflectionProperty[]$ref->getAttributes(); // Attribute der Klasse als ReflectionAttribute[]
Der IoC-Container von Laravel implementiert Konstruktor-Injection (automatische Auflösung von Abhängigkeiten) über die Reflection API. So funktioniert app()->make(SomeClass::class) und die Dependency Injection.
Die tatsächliche build()-Methode in Container.php sieht in etwa so aus:
// Vereinfachte Illuminate\Container\Container::build()public function build($concrete){ // 1. Klasse mit ReflectionClass inspizieren $reflector = new ReflectionClass($concrete); // Nicht instanziierbare Klassen (Interface, Abstract usw.) → Fehler if (! $reflector->isInstantiable()) { throw new BindingResolutionException("[$concrete] is not instantiable."); } // 2. Konstruktor ermitteln $constructor = $reflector->getConstructor(); // Kein Konstruktor → direkt instanziieren if (is_null($constructor)) { return new $concrete; } // 3. Alle Konstruktorparameter ermitteln $dependencies = $constructor->getParameters(); // 4. Jedes Argument rekursiv auflösen $instances = $this->resolveDependencies($dependencies); return new $concrete(...$instances);}protected function resolveDependencies(array $dependencies): array{ $results = []; foreach ($dependencies as $dependency) { // Bei vorhandenem Type-Hint rekursiv über Container auflösen $className = Util::getParameterClassName($dependency); $results[] = is_null($className) ? $this->resolvePrimitive($dependency) // Primitivtyp : $this->resolveClass($dependency, $className); // Klassentyp } return $results;}
Util::getParameterClassName() extrahiert den Typnamen aus dem Ergebnis von $parameter->getType(). Es umhüllt den von ReflectionParameter::getType() gelieferten ReflectionNamedType bequemer.
Ab PHP 8.0 lassen sich Attribute an Klassen, Methoden und Properties über die Reflection API auslesen. Laravel verarbeitet damit Queue- und Eloquent-Attribute.
Für den Einsatz von PHP-Attributen in Laravel siehe auch PHP-Attribute.
Metadaten sammeln — Routen automatisch per Attribut registrieren
Ein Muster, in dem Attribute und Reflection kombiniert werden, um Routen automatisch einzusammeln.
// Eigenes Route-Attribut#[\Attribute(\Attribute::TARGET_METHOD)]class Route{ public function __construct( public string $method, public string $path, ) {}}// Verwendung im Controllerclass UserController{ #[Route('GET', '/users')] public function index() { /* ... */ } #[Route('POST', '/users')] public function store() { /* ... */ }}// Service Provider, der Routen automatisch aus Attributen registriertclass AttributeRouteServiceProvider extends ServiceProvider{ public function boot(Router $router): void { $ref = new ReflectionClass(UserController::class); foreach ($ref->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { foreach ($method->getAttributes(Route::class) as $attr) { $route = $attr->newInstance(); $router->addRoute( $route->method, $route->path, [UserController::class, $method->getName()], ); } } }}
Die Reflection API parst Klasseninformationen jedes Mal neu, was Kosten verursacht. In Produktionscode empfiehlt es sich, Ergebnisse zu cachen.
class ReflectionCache{ private static array $cache = []; public static function getClass(string $class): \ReflectionClass { return self::$cache[$class] ??= new \ReflectionClass($class); }}// Anwendungsbeispiel$ref = ReflectionCache::getClass(UserController::class);
PHP OPcache cacht keine Reflection-Ergebnisse. Wenn Sie viele Klassen in einer Schleife untersuchen, sollten Sie eigenes Caching einbauen. Der Laravel-Container selbst wiederverwendet innerhalb desselben Requests bereits ReflectionClass-Instanzen.