La Reflection API de PHP es una funcionalidad nativa que permite obtener e inspeccionar en tiempo de ejecución la metainformación de clases, métodos, propiedades, funciones y parámetros. Puedes saber qué argumentos recibe el constructor de una clase o qué atributos tiene un método sin modificar el código fuente.Laravel utiliza intensamente la Reflection API dentro de Illuminate/Container/Container.php para implementar la resolución automática del contenedor DI, la lectura de atributos de PHP, la inyección de dependencias por método, etc.
$ref = new ReflectionClass(UserController::class);$ref->getName(); // Nombre totalmente cualificado$ref->getShortName(); // Solo el nombre de la clase$ref->isInstantiable(); // Si es instanciable$ref->getConstructor(); // Devuelve el constructor como ReflectionMethod$ref->getMethods(); // Devuelve todos los métodos como ReflectionMethod[]$ref->getProperties(); // Devuelve todas las propiedades como ReflectionProperty[]$ref->getAttributes(); // Devuelve los atributos de la clase como ReflectionAttribute[]
ReflectionParameter — inspeccionar los argumentos del constructor
$ref = new ReflectionClass(UserController::class);$constructor = $ref->getConstructor();if ($constructor) { foreach ($constructor->getParameters() as $param) { $param->getName(); // Nombre del argumento $param->getType(); // Type hint (ReflectionType) $param->isOptional(); // Si es opcional $param->isVariadic(); // Si es variádico $param->getDefaultValue(); // Valor por defecto (si existe) }}
El contenedor IoC de Laravel usa la Reflection API para lograr la inyección por constructor (resolución automática de dependencias). Comprendamos cómo funcionan app()->make(SomeClass::class) y la inyección de dependencias.
Método build() del contenedor (versión simplificada)
El método build() real de Container.php es aproximadamente así:
// Illuminate\Container\Container::build() simplificadopublic function build($concrete){ // 1. Inspeccionar la clase con ReflectionClass $reflector = new ReflectionClass($concrete); // Las clases no instanciables (interfaces, abstractas...) dan error if (! $reflector->isInstantiable()) { throw new BindingResolutionException("[$concrete] is not instantiable."); } // 2. Obtener el constructor $constructor = $reflector->getConstructor(); // Sin constructor → instanciar directamente if (is_null($constructor)) { return new $concrete; } // 3. Obtener todos los parámetros del constructor $dependencies = $constructor->getParameters(); // 4. Resolver cada parámetro recursivamente $instances = $this->resolveDependencies($dependencies); return new $concrete(...$instances);}protected function resolveDependencies(array $dependencies): array{ $results = []; foreach ($dependencies as $dependency) { // Si hay type hint, resolver recursivamente a través del contenedor $className = Util::getParameterClassName($dependency); $results[] = is_null($className) ? $this->resolvePrimitive($dependency) // Tipo primitivo : $this->resolveClass($dependency, $className); // Tipo clase } return $results;}
Util::getParameterClassName() es una utilidad que extrae el nombre del tipo como cadena a partir del resultado de $parameter->getType(). Envuelve el ReflectionNamedType de ReflectionParameter::getType() para hacerlo más fácil de usar.
Desde PHP 8.0, la Reflection API permite obtener los atributos aplicados a clases, métodos y propiedades. Laravel usa este mecanismo para procesar sus atributos de cola y de Eloquent.
Para más información sobre los atributos de PHP y su integración en Laravel, consulta Atributos de PHP.
use ReflectionClass;// 1. Obtener los atributos aplicados a la clase$ref = new ReflectionClass(ProcessOrder::class);$attrs = $ref->getAttributes(Queue::class); // Solo el atributo indicadoforeach ($attrs as $attr) { $instance = $attr->newInstance(); // Instancia la clase de atributo echo $instance->queue; // Lee la propiedad}// 2. Todos los atributos (sin filtro)$allAttrs = $ref->getAttributes();foreach ($allAttrs as $attr) { echo $attr->getName(); // Nombre de clase (FQCN) del atributo print_r($attr->getArguments()); // Argumentos del constructor}
Obtener metadatos — registrar rutas automáticamente con atributos
Patrón que combina atributos con Reflection para recolectar rutas de forma automática.
// Definición del atributo Route#[\Attribute(\Attribute::TARGET_METHOD)]class Route{ public function __construct( public string $method, public string $path, ) {}}// Uso en el controladorclass UserController{ #[Route('GET', '/users')] public function index() { /* ... */ } #[Route('POST', '/users')] public function store() { /* ... */ }}// Service provider que registra rutas a partir de los atributosclass 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()], ); } } }}
La Reflection API tiene coste porque parsea la información de la clase cada vez. En código de producción es buena práctica cachear los resultados.
class ReflectionCache{ private static array $cache = []; public static function getClass(string $class): \ReflectionClass { return self::$cache[$class] ??= new \ReflectionClass($class); }}// Uso$ref = ReflectionCache::getClass(UserController::class);
OPcache no cachea los resultados de Reflection. Si vas a inspeccionar muchas clases en bucle, plantéate una caché propia. Aun así, el propio contenedor de Laravel reutiliza las instancias de ReflectionClass dentro de la misma petición.