> ## 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`  | 프로퍼티의 타입·기본값·어트리뷰트를 취득       |
| `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를 사용해 클래스·메서드·프로퍼티에 붙은 어트리뷰트를 취득할 수 있습니다. Laravel은 이 구조를 사용해 큐 어트리뷰트나 Eloquent 어트리뷰트를 처리하고 있습니다.

<Tip>
  PHP 어트리뷰트와 Laravel에의 짜넣기에 대해서는 [PHP 어트리뷰트](/ko/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,
    ) {}
}

// コントローラーで使う
class UserController
{
    #[Route('GET', '/users')]
    public function index() { /* ... */ }

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

// アトリビュートからルートを自動登録するサービスプロバイダー
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="/ko/advanced/php-attributes">
    ReflectionClass::getAttributes()를 사용해 읽어들이는 PHP 어트리뷰트의 상세를 배웁니다.
  </Card>

  <Card title="패키지 개발" icon="package" href="/ko/advanced/package-development">
    Reflection API를 활용한 Laravel 패키지의 개발 방법을 배웁니다.
  </Card>
</Columns>


## Related topics

- [PHP 어트리뷰트](/ko/advanced/php-attributes.md)
- [API 레퍼런스 - VOICEVOX Core for PHP](/ko/packages/voicevox-core-php/api.md)
- [Laravel 11 이후의 새 앱 구조 FAQ](/ko/advanced/app-structure-faq.md)
- [Google Sheets API for Laravel](/ko/packages/laravel-google-sheets/index.md)
- [PHP FFI](/ko/advanced/ffi.md)
