Passer au contenu principal

Qu’est-ce que l’API PHP Reflection

L’API PHP Reflection est une fonctionnalité intégrée à PHP permettant d’obtenir et d’inspecter à l’exécution les méta-informations des classes, méthodes, propriétés, fonctions ou paramètres. Vous pouvez ainsi vérifier les arguments du constructeur d’une classe, ou les attributs posés sur une méthode, sans modifier directement le code source. Laravel utilise abondamment l’API Reflection à l’intérieur de Illuminate/Container/Container.php pour la résolution automatique du conteneur DI, la lecture des attributs PHP ou l’injection dans les méthodes.

Classes principales

ClasseUsage principal
ReflectionClassPoint d’entrée pour obtenir les méta-informations d’une classe
ReflectionMethodRécupère les arguments, modificateurs et attributs d’une méthode
ReflectionPropertyRécupère le type, la valeur par défaut et les attributs d’une propriété
ReflectionParameterRécupère les informations sur un paramètre (hint de type, valeur par défaut)
ReflectionFunctionRécupère les méta-informations d’une fonction ou closure
ReflectionAttributeRécupère le nom de classe et les arguments d’un attribut

ReflectionClass — obtenir des informations sur une classe

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

$ref->getName();          // Nom complètement qualifié de la classe
$ref->getShortName();     // Nom court de la classe
$ref->isInstantiable();   // Peut-elle être instanciée ?
$ref->getConstructor();   // Retourne le constructeur en tant que ReflectionMethod
$ref->getMethods();       // Retourne toutes les méthodes en ReflectionMethod[]
$ref->getProperties();    // Retourne toutes les propriétés en ReflectionProperty[]
$ref->getAttributes();    // Retourne les attributs de la classe en ReflectionAttribute[]

ReflectionParameter — inspecter les arguments d’un constructeur

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

if ($constructor) {
    foreach ($constructor->getParameters() as $param) {
        $param->getName();           // Nom de l'argument
        $param->getType();           // Hint de type (ReflectionType)
        $param->isOptional();        // Optionnel ?
        $param->isVariadic();        // Variadique ?
        $param->getDefaultValue();   // Valeur par défaut (si présente)
    }
}

Conteneur Laravel et API Reflection

Le conteneur IoC de Laravel s’appuie sur l’API Reflection pour l’injection par constructeur (résolution automatique des dépendances). Comprenons le mécanisme derrière app()->make(SomeClass::class) et l’injection de dépendances.

La méthode build() du conteneur (version simplifiée)

Le vrai build() de Container.php ressemble à peu près à ceci.
// Simplification de Illuminate\Container\Container::build()
public function build($concrete)
{
    // 1. Inspection de la classe via ReflectionClass
    $reflector = new ReflectionClass($concrete);

    // Les classes non instanciables (interface, abstract, etc.) provoquent une erreur
    if (! $reflector->isInstantiable()) {
        throw new BindingResolutionException("[$concrete] is not instantiable.");
    }

    // 2. Récupérer le constructeur
    $constructor = $reflector->getConstructor();

    // Pas de constructeur → instancier tel quel
    if (is_null($constructor)) {
        return new $concrete;
    }

    // 3. Récupérer tous les paramètres du constructeur
    $dependencies = $constructor->getParameters();

    // 4. Résoudre chaque paramètre de manière récursive
    $instances = $this->resolveDependencies($dependencies);

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

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

    foreach ($dependencies as $dependency) {
        // Si un hint de type est disponible, résolution récursive via le conteneur
        $className = Util::getParameterClassName($dependency);

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

    return $results;
}
Util::getParameterClassName() est un utilitaire qui extrait le nom du type sous forme de chaîne à partir du résultat de $parameter->getType(). Il enveloppe le ReflectionNamedType renvoyé par ReflectionParameter::getType() de manière plus commode.

Lire les attributs PHP

Depuis PHP 8.0, l’API Reflection permet de récupérer les attributs posés sur des classes, méthodes ou propriétés. Laravel s’en sert pour traiter les attributs de queue ou d’Eloquent.
Pour l’intégration des attributs PHP dans Laravel, voir aussi Attributs PHP.

Schéma de base pour lire un attribut

use ReflectionClass;

// 1. Récupérer les attributs posés sur la classe
$ref = new ReflectionClass(ProcessOrder::class);
$attrs = $ref->getAttributes(Queue::class); // Ne récupère que l'attribut donné

foreach ($attrs as $attr) {
    $instance = $attr->newInstance(); // Instancie la classe d'attribut
    echo $instance->queue;            // Lit une propriété de l'attribut
}

// 2. Récupérer tous les attributs (sans filtre)
$allAttrs = $ref->getAttributes();

foreach ($allAttrs as $attr) {
    echo $attr->getName();       // Nom (FQCN) de la classe d'attribut
    print_r($attr->getArguments()); // Arguments du constructeur
}

Comment Laravel lit l’attribut Queue (version simplifiée)

// Simplification de ReadsQueueAttributes dans le 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;
    }
}

Lire les attributs d’une méthode

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

Utilisations en développement de packages

Vérifier les interfaces implémentées par une classe

Vous pouvez vérifier dynamiquement si une classe implémente une interface donnée depuis votre package.
use ReflectionClass;

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

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

Récupération de métadonnées — enregistrer automatiquement des routes via attributs

Un schéma combinant attributs et Reflection pour collecter automatiquement les routes.
// Définition d'un attribut Route maison
#[\Attribute(\Attribute::TARGET_METHOD)]
class Route
{
    public function __construct(
        public string $method,
        public string $path,
    ) {}
}

// Utilisation dans un contrôleur
class UserController
{
    #[Route('GET', '/users')]
    public function index() { /* ... */ }

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

// Service provider qui enregistre les routes à partir des attributs
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()],
                );
            }
        }
    }
}

Appel dynamique de méthode — injection de méthode

La méthode call() de Laravel utilise Reflection pour résoudre automatiquement les arguments. Voici comment implémenter le même mécanisme dans un package.
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);
}

// Exemple d'utilisation
callWithDependencies([new UserController(), 'index'], app());

Récupérer les valeurs par défaut des propriétés

Schéma pour récupérer les valeurs par défaut d’une classe de configuration via Reflection.
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);

Attention aux performances

L’API Reflection parse les informations de classe à chaque appel : cela a un coût. En production, la bonne pratique est de mettre les résultats en cache.
class ReflectionCache
{
    private static array $cache = [];

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

// Exemple d'utilisation
$ref = ReflectionCache::getClass(UserController::class);
L’OPcache de PHP ne met pas en cache les résultats de Reflection. Si vous inspectez de nombreuses classes en boucle, envisagez un cache personnalisé. Notez toutefois que le conteneur Laravel réutilise déjà l’instance ReflectionClass dans la même requête.

Étapes suivantes

Attributs PHP

Découvrez en détail les attributs PHP lus via ReflectionClass::getAttributes().

Développement de packages

Découvrez comment développer un package Laravel en tirant parti de l’API Reflection.
Dernière modification le 13 juillet 2026