Vai al contenuto principale

Cos’è la PHP Reflection API

La PHP Reflection API è una funzionalità integrata di PHP che permette di ottenere e ispezionare a runtime le meta-informazioni di classi, metodi, proprietà, funzioni e parametri. Puoi scoprire quali argomenti riceve un costruttore o quali attributi sono applicati a un metodo, senza modificare il codice sorgente. Laravel usa massicciamente la Reflection API dentro Illuminate/Container/Container.php per realizzare la risoluzione automatica del DI container, la lettura degli attributi PHP, la method injection e altro ancora.

Classi principali

ClasseUso principale
ReflectionClassPunto d’ingresso per le meta-info di una classe
ReflectionMethodOttiene argomenti, modificatori d’accesso e attributi di un metodo
ReflectionPropertyOttiene tipo, valore di default e attributi di una proprietà
ReflectionParameterOttiene info sui parametri di metodi/funzioni (type hint, default)
ReflectionFunctionOttiene meta-info di funzioni e closure
ReflectionAttributeOttiene nome classe e argomenti di un attributo

ReflectionClass — informazioni sulla classe

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

$ref->getName();          // nome fully qualified della classe
$ref->getShortName();     // solo il nome della classe
$ref->isInstantiable();   // se è istanziabile
$ref->getConstructor();   // restituisce il costruttore come ReflectionMethod
$ref->getMethods();       // restituisce tutti i metodi come ReflectionMethod[]
$ref->getProperties();    // restituisce tutte le proprietà come ReflectionProperty[]
$ref->getAttributes();    // restituisce gli attributi come ReflectionAttribute[]

ReflectionParameter — ispezionare gli argomenti del costruttore

$ref = new ReflectionClass(UserController::class);
$constructor = $ref->getConstructor();

if ($constructor) {
    foreach ($constructor->getParameters() as $param) {
        $param->getName();           // nome del parametro
        $param->getType();           // type hint (ReflectionType)
        $param->isOptional();        // se è opzionale
        $param->isVariadic();        // se è variadic
        $param->getDefaultValue();   // valore di default (se presente)
    }
}

Container di Laravel e Reflection API

L’IoC container di Laravel realizza la constructor injection (risoluzione automatica delle dipendenze) usando la Reflection API. Vediamo il meccanismo alla base di app()->make(SomeClass::class) e della dependency injection.

Metodo build() del container (semplificato)

Il metodo build() reale di Container.php è più o meno così.
// Illuminate\Container\Container::build() semplificato
public function build($concrete)
{
    // 1. Ispeziona la classe con ReflectionClass
    $reflector = new ReflectionClass($concrete);

    // Le classi non istanziabili (interface, abstract, ecc.) sollevano errore
    if (! $reflector->isInstantiable()) {
        throw new BindingResolutionException("[$concrete] is not instantiable.");
    }

    // 2. Ottiene il costruttore
    $constructor = $reflector->getConstructor();

    // Nessun costruttore → istanzia direttamente
    if (is_null($constructor)) {
        return new $concrete;
    }

    // 3. Ottiene tutti i parametri del costruttore
    $dependencies = $constructor->getParameters();

    // 4. Risolve ricorsivamente ciascun parametro
    $instances = $this->resolveDependencies($dependencies);

    return new $concrete(...$instances);
}

protected function resolveDependencies(array $dependencies): array
{
    $results = [];

    foreach ($dependencies as $dependency) {
        // Se c'è un type hint, risolve ricorsivamente col container
        $className = Util::getParameterClassName($dependency);

        $results[] = is_null($className)
            ? $this->resolvePrimitive($dependency)  // tipo primitivo
            : $this->resolveClass($dependency, $className); // tipo classe
    }

    return $results;
}
Util::getParameterClassName() è una utility che estrae la stringa del tipo dal risultato di $parameter->getType(). Fa da wrapper conveniente al ReflectionNamedType restituito da ReflectionParameter::getType().

Lettura degli attributi PHP

Da PHP 8.0, tramite la Reflection API puoi ottenere gli attributi applicati a classi, metodi e proprietà. Laravel usa questo meccanismo per gestire gli attributi delle code e di Eloquent.
Per gli attributi PHP e la loro integrazione in Laravel, consulta anche Attributi PHP.

Pattern base per leggere gli attributi

use ReflectionClass;

// 1. ottieni gli attributi applicati alla classe
$ref = new ReflectionClass(ProcessOrder::class);
$attrs = $ref->getAttributes(Queue::class); // ottieni solo un attributo specifico

foreach ($attrs as $attr) {
    $instance = $attr->newInstance(); // istanzia la classe attributo
    echo $instance->queue;            // leggi le proprietà dell'attributo
}

// 2. ottieni tutti gli attributi (senza filtro)
$allAttrs = $ref->getAttributes();

foreach ($allAttrs as $attr) {
    echo $attr->getName();       // nome classe attributo (FQCN)
    print_r($attr->getArguments()); // argomenti del costruttore
}

Come Laravel legge gli attributi Queue (semplificato)

// semplificato da 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;
    }
}

Leggere gli attributi dei metodi

$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}";
    }
}

Utilizzi nello sviluppo di pacchetti

Verificare le interfacce implementate da una classe

In un pacchetto puoi verificare dinamicamente se una classe implementa una specifica interfaccia.
use ReflectionClass;

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

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

Ottenere metadati — registrazione automatica delle rotte via attributi

Pattern per raccogliere e registrare rotte in modo automatico combinando attributi e Reflection.
// definizione dell'attributo Route custom
#[\Attribute(\Attribute::TARGET_METHOD)]
class Route
{
    public function __construct(
        public string $method,
        public string $path,
    ) {}
}

// uso nel controller
class UserController
{
    #[Route('GET', '/users')]
    public function index() { /* ... */ }

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

// service provider che registra automaticamente le rotte dagli attributi
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()],
                );
            }
        }
    }
}

Chiamata dinamica di metodo — method injection

Il metodo call() di Laravel risolve gli argomenti automaticamente con la Reflection. Esempio di come implementare lo stesso meccanismo in un pacchetto.
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);
}

// esempio d'uso
callWithDependencies([new UserController(), 'index'], app());

Raccogliere i valori di default delle proprietà

Pattern per ottenere via Reflection i valori di default di una classe di configurazione.
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);

Considerazioni sulle performance

La Reflection API riparsa ogni volta le info di classe, quindi ha un costo. Nella produzione è best practice mettere in cache i risultati.
class ReflectionCache
{
    private static array $cache = [];

    public static function getClass(string $class): \ReflectionClass
    {
        return self::$cache[$class] ??= new \ReflectionClass($class);
    }
}

// esempio d'uso
$ref = ReflectionCache::getClass(UserController::class);
L’OPcache di PHP non memorizza in cache i risultati della Reflection. Se ispezioni molte classi in loop, valuta una cache tua. Nota però che il container di Laravel già riutilizza le istanze di ReflectionClass all’interno della stessa richiesta.

Prossimi passi

Attributi PHP

Approfondisci gli attributi PHP letti tramite ReflectionClass::getAttributes().

Sviluppo di pacchetti

Impara a sviluppare pacchetti Laravel sfruttando la Reflection API.
Ultima modifica il 13 luglio 2026