Saltar al contenido principal

Qué es la Reflection API de PHP

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.

Clases principales

ClaseUso principal
ReflectionClassPunto de partida para obtener metainformación de la clase.
ReflectionMethodArgumentos, modificadores de acceso y atributos de un método.
ReflectionPropertyTipo, valor por defecto y atributos de una propiedad.
ReflectionParameterInformación de los argumentos de métodos/funciones (type hints, defaults).
ReflectionFunctionMetainformación de funciones y closures.
ReflectionAttributeNombre de clase y argumentos de un atributo.

ReflectionClass — obtener información de la clase

$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 de Laravel y la Reflection API

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() simplificado
public 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.

Lectura de atributos de PHP

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.

Patrón básico de lectura de atributos

use ReflectionClass;

// 1. Obtener los atributos aplicados a la clase
$ref = new ReflectionClass(ProcessOrder::class);
$attrs = $ref->getAttributes(Queue::class); // Solo el atributo indicado

foreach ($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
}

Cómo Laravel lee el atributo Queue (versión simplificada)

// Simplificado del ReadsQueueAttributes del trait InteractsWithQueue
protected function setJobInstanceForQueue(object $job): void
{
    $reflection = new ReflectionClass($job);

    foreach ($reflection->getAttributes(Queue::class) as $attribute) {
        $instance = $attribute->newInstance();
        $job->queue = $instance->queue instanceof \UnitEnum
            ? $instance->queue->value
            : $instance->queue;
    }
}

Leer atributos de un método

$ref = new ReflectionClass(UserController::class);

foreach ($ref->getMethods() as $method) {
    $attrs = $method->getAttributes(Route::class);

    foreach ($attrs as $attr) {
        $route = $attr->newInstance();
        echo "{$method->getName()} => {$route->path}";
    }
}

Ejemplos en el desarrollo de paquetes

Comprobar las interfaces que implementa una clase

En un paquete puedes verificar dinámicamente si una clase implementa una interfaz concreta.
use ReflectionClass;

function isQueueable(string $class): bool
{
    $ref = new ReflectionClass($class);

    return $ref->implementsInterface(\Illuminate\Contracts\Queue\ShouldQueue::class);
}

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 controlador
class UserController
{
    #[Route('GET', '/users')]
    public function index() { /* ... */ }

    #[Route('POST', '/users')]
    public function store() { /* ... */ }
}

// Service provider que registra rutas a partir de los atributos
class 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()],
                );
            }
        }
    }
}

Invocación dinámica de métodos — method injection

El método call() de Laravel resuelve los argumentos automáticamente mediante Reflection. Ejemplo para implementar el mismo mecanismo en un paquete.
use ReflectionFunction;
use ReflectionMethod;

function callWithDependencies(callable $callable, Container $container): mixed
{
    if (is_array($callable)) {
        [$object, $method] = $callable;
        $ref = new ReflectionMethod($object, $method);
        $params = $ref->getParameters();
    } else {
        $ref = new ReflectionFunction($callable);
        $params = $ref->getParameters();
    }

    $args = [];
    foreach ($params as $param) {
        $type = $param->getType()?->getName();
        $args[] = $type ? $container->make($type) : null;
    }

    return $callable(...$args);
}

// Uso
callWithDependencies([new UserController(), 'index'], app());

Recoger los valores por defecto de las propiedades

Patrón para obtener con Reflection los valores por defecto de una clase de configuración.
use ReflectionClass;
use ReflectionProperty;

function getDefaults(string $class): array
{
    $ref = new ReflectionClass($class);
    $defaults = [];

    foreach ($ref->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
        if ($prop->hasDefaultValue()) {
            $defaults[$prop->getName()] = $prop->getDefaultValue();
        }
    }

    return $defaults;
}

class DatabaseConfig
{
    public string $driver = 'mysql';
    public int $port = 3306;
    public bool $strict = true;
}

// ['driver' => 'mysql', 'port' => 3306, 'strict' => true]
$defaults = getDefaults(DatabaseConfig::class);

Consideraciones de rendimiento

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.

Próximos pasos

Atributos de PHP

Detalles de los atributos de PHP que se leen con ReflectionClass::getAttributes().

Desarrollo de paquetes

Cómo desarrollar paquetes Laravel aprovechando la Reflection API.
Última modificación el 13 de julio de 2026