> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# PHP Reflection API

> 讲解 PHP Reflection API 的机制，及其在 Laravel IoC 容器与包开发中的应用。

## 什么是 PHP Reflection API

PHP Reflection API 是 PHP 内置的能力，可以在运行时获取并检查类、方法、属性、函数、参数等的元信息。可以在不改动源码的情况下探知一个类的构造函数接受哪些参数、方法上挂了哪些属性等等。

Laravel 在 `Illuminate/Container/Container.php` 中大量使用了 Reflection API，实现了 DI 容器的自动解析、PHP 属性的读取、方法注入等能力。

## 主要类

| 类                     | 主要用途                  |
| --------------------- | --------------------- |
| `ReflectionClass`     | 获取类元信息的入口             |
| `ReflectionMethod`    | 获取方法的参数、访问修饰符、属性      |
| `ReflectionProperty`  | 获取属性的类型、默认值、attribute |
| `ReflectionParameter` | 获取方法、函数参数信息（类型提示、默认值） |
| `ReflectionFunction`  | 获取函数、闭包的元信息           |
| `ReflectionAttribute` | 获取属性的类名与参数            |

### ReflectionClass — 获取类信息

```php theme={null}
$ref = new ReflectionClass(UserController::class);

$ref->getName();          // 完全限定类名
$ref->getShortName();     // 仅类名
$ref->isInstantiable();   // 是否可实例化
$ref->getConstructor();   // 返回构造函数（ReflectionMethod）
$ref->getMethods();       // 返回所有方法 ReflectionMethod[]
$ref->getProperties();    // 返回所有属性 ReflectionProperty[]
$ref->getAttributes();    // 返回类上的属性 ReflectionAttribute[]
```

### ReflectionParameter — 检查构造函数参数

```php theme={null}
$ref = new ReflectionClass(UserController::class);
$constructor = $ref->getConstructor();

if ($constructor) {
    foreach ($constructor->getParameters() as $param) {
        $param->getName();           // 参数名
        $param->getType();           // 类型提示（ReflectionType）
        $param->isOptional();        // 是否可省略
        $param->isVariadic();        // 是否可变参数
        $param->getDefaultValue();   // 默认值（若存在）
    }
}
```

## Laravel 容器与 Reflection API

Laravel 的 IoC 容器利用 Reflection API 实现构造函数注入（依赖自动解析）。理解一下 `app()->make(SomeClass::class)` 与依赖注入的工作原理。

```mermaid theme={null}
sequenceDiagram
    participant App as 应用代码
    participant Container as IoC 容器
    participant Reflection as ReflectionClass
    participant Dep as 依赖类

    App->>Container: app()->make(UserController::class)
    Container->>Reflection: new ReflectionClass(UserController::class)
    Reflection-->>Container: 构造函数信息
    Container->>Reflection: getConstructor()->getParameters()
    Reflection-->>Container: [UserRepository, Cache, ...]
    loop 解析每个参数
        Container->>Container: 按类型提示递归 make()
        Container->>Dep: new Dep(...)
        Dep-->>Container: 实例
    end
    Container-->>App: new UserController(repository, cache, ...)
```

### 容器的 `build()` 方法（简化版）

`Container.php` 中真正的 `build()` 方法大致是这样的：

```php theme={null}
// Illuminate\Container\Container::build() 简化
public function build($concrete)
{
    // 1. 用 ReflectionClass 检查类
    $reflector = new ReflectionClass($concrete);

    // 无法实例化的类（interface、abstract 等）报错
    if (! $reflector->isInstantiable()) {
        throw new BindingResolutionException("[$concrete] is not instantiable.");
    }

    // 2. 获取构造函数
    $constructor = $reflector->getConstructor();

    // 没有构造函数 → 直接实例化
    if (is_null($constructor)) {
        return new $concrete;
    }

    // 3. 获取构造函数全部参数
    $dependencies = $constructor->getParameters();

    // 4. 递归解析每个参数
    $instances = $this->resolveDependencies($dependencies);

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

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

    foreach ($dependencies as $dependency) {
        // 能取到类型提示时通过容器递归解析
        $className = Util::getParameterClassName($dependency);

        $results[] = is_null($className)
            ? $this->resolvePrimitive($dependency)  // 原始类型
            : $this->resolveClass($dependency, $className); // 类类型
    }

    return $results;
}
```

<Info>
  `Util::getParameterClassName()` 是从 `$parameter->getType()` 提取类型名字符串的工具。它将 `ReflectionParameter::getType()` 返回的 `ReflectionNamedType` 做了便捷封装。
</Info>

## 读取 PHP 属性

PHP 8.0 及以后，可以通过 Reflection API 读取类、方法、属性上挂的 PHP 属性。Laravel 利用该机制来处理队列属性与 Eloquent 属性。

<Tip>
  关于 PHP 属性及其在 Laravel 中的集成，请一并参考 [PHP 属性](/zh/advanced/php-attributes)。
</Tip>

### 读取属性的基本模式

```php theme={null}
use ReflectionClass;

// 1. 读取类上的属性
$ref = new ReflectionClass(ProcessOrder::class);
$attrs = $ref->getAttributes(Queue::class); // 仅获取特定属性

foreach ($attrs as $attr) {
    $instance = $attr->newInstance(); // 实例化属性类
    echo $instance->queue;            // 读取属性字段
}

// 2. 获取所有属性（不过滤）
$allAttrs = $ref->getAttributes();

foreach ($allAttrs as $attr) {
    echo $attr->getName();       // 属性类名（FQCN）
    print_r($attr->getArguments()); // 构造函数参数
}
```

### Laravel 读取 Queue 属性的机制（简化）

```php theme={null}
// 从 InteractsWithQueue 的 ReadsQueueAttributes 简化
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;
    }
}
```

### 读取方法上的属性

```php theme={null}
$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}";
    }
}
```

## 包开发中的应用示例

### 检查类实现的接口

在包中可以动态确认某个类是否实现了特定接口。

```php theme={null}
use ReflectionClass;

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

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

### 元数据获取 — 通过属性自动注册路由

结合属性与 Reflection 自动收集路由的模式。

```php theme={null}
// 自定义 Route 属性定义
#[\Attribute(\Attribute::TARGET_METHOD)]
class Route
{
    public function __construct(
        public string $method,
        public string $path,
    ) {}
}

// 在 Controller 中使用
class UserController
{
    #[Route('GET', '/users')]
    public function index() { /* ... */ }

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

// 从属性自动注册路由的 Service Provider
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()],
                );
            }
        }
    }
}
```

### 动态方法调用 — 方法注入

Laravel 的 `call()` 方法利用 Reflection 自动解析参数。以下是在包中实现相同机制的示例。

```php theme={null}
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);
}

// 使用示例
callWithDependencies([new UserController(), 'index'], app());
```

### 收集属性的默认值

通过 Reflection 获取配置类的默认值的模式。

```php theme={null}
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);
```

## 性能方面的注意

Reflection API 每次都会解析类信息，成本较高。生产代码中缓存结果是最佳实践。

```php theme={null}
class ReflectionCache
{
    private static array $cache = [];

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

// 使用示例
$ref = ReflectionCache::getClass(UserController::class);
```

<Warning>
  PHP 的 OPcache 不会缓存 Reflection 结果。若在循环中检查大量类，请考虑自定义缓存。不过 Laravel 容器本身在同一请求内会复用 `ReflectionClass` 实例。
</Warning>

## 下一步

<Columns cols={2}>
  <Card title="PHP 属性" icon="tag" href="/zh/advanced/php-attributes">
    学习通过 ReflectionClass::getAttributes() 读取 PHP 属性的详情。
  </Card>

  <Card title="包开发" icon="package" href="/zh/advanced/package-development">
    了解利用 Reflection API 的 Laravel 包开发方法。
  </Card>
</Columns>


## Related topics

- [PHP 属性](/zh/advanced/php-attributes.md)
- [API 参考 - VOICEVOX Core for PHP](/zh/packages/voicevox-core-php/api.md)
- [Laravel 11 之后新应用结构 FAQ](/zh/advanced/app-structure-faq.md)
- [PHP FFI](/zh/advanced/ffi.md)
- [Google Sheets API for Laravel](/zh/packages/laravel-google-sheets/index.md)
