Skip to content

ReflectionClass

概述

ReflectionClass 是 PHP 反射 API 中最核心的类之一,用于在运行时获取类的完整信息。通过 ReflectionClass,你可以检查类的名称、命名空间、属性、方法、常量、接口实现、父类关系等,甚至可以动态创建类的实例。它是框架开发中依赖注入容器、ORM 映射、中间件系统的基础。

核心能力

ReflectionClass 提供了类的完整自省能力:获取类信息、检查继承关系、实例化对象、调用方法。

基础概念

获取类信息

通过 new ReflectionClass(ClassName::class)new ReflectionClass($object) 创建反射实例。

类的继承关系检查

isSubclassOf()implementsInterface() 用于检查类的继承和实现关系。

动态实例化

newInstance()newInstanceArgs() 可以在运行时创建类的实例。

语法与代码

基本 ReflectionClass 用法

php
<?php
declare(strict_types=1);

class ProductService
{
    public function __construct(
        private readonly string $tableName = 'products'
    ) {}

    public function findById(int $id): ?array
    {
        return ['id' => $id, 'name' => 'Product'];
    }

    public function findAll(): array
    {
        return [];
    }

    protected function validate(array $data): bool
    {
        return true;
    }

    private function log(string $message): void {}
}

$refClass = new \ReflectionClass(ProductService::class);

echo "完整类名: " . $refClass->getName() . "\n";
echo "短类名: " . $refClass->getShortName() . "\n";
echo "命名空间: " . $refClass->getNamespaceName() . "\n";
echo "文件名: " . $refClass->getFileName() . "\n";
echo "起始行: " . $refClass->getStartLine() . "\n";
echo "结束行: " . $refClass->getEndLine() . "\n";
echo "是否为内置类: " . ($refClass->isInternal() ? 'yes' : 'no') . "\n";
echo "是否可实例化: " . ($refClass->isInstantiable() ? 'yes' : 'no') . "\n";
echo "是否为抽象类: " . ($refClass->isAbstract() ? 'yes' : 'no') . "\n";
echo "是否为 final 类: " . ($refClass->isFinal() ? 'yes' : 'no') . "\n";
echo "是否为 trait: " . ($refClass->isTrait() ? 'yes' : 'no') . "\n";

获取类的属性列表

php
<?php
declare(strict_types=1);

class Order
{
    public int $id = 0;
    public string $status = 'pending';
    protected float $total = 0.0;
    private bool $paid = false;
    public static int $count = 0;

    public const STATUS_PENDING = 'pending';
    public const STATUS_COMPLETED = 'completed';
}

$refClass = new \ReflectionClass(Order::class);

echo "默认属性:\n";
foreach ($refClass->getProperties() as $prop) {
    $modifiers = \Reflection::getModifierNames($prop->getModifiers());
    echo "  " . implode(' ', $modifiers) . " \${$prop->getName()}\n";
}

echo "\n类常量:\n";
foreach ($refClass->getConstants() as $name => $value) {
    echo "  {$name} = {$value}\n";
}

获取类的方法列表

php
<?php
declare(strict_types=1);

$refClass = new \ReflectionClass(ProductService::class);

echo "所有方法:\n";
foreach ($refClass->getMethods() as $method) {
    $modifiers = \Reflection::getModifierNames($method->getModifiers());
    $returnType = $method->getReturnType()?->getName() ?? 'void';
    echo "  " . implode(' ', $modifiers) . " {$method->getName()}(): {$returnType}\n";
}

echo "\n仅公共方法:\n";
foreach ($refClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
    echo "  " . $method->getName() . "()\n";
}

检查继承关系

php
<?php
declare(strict_types=1);

interface Loggable {}

interface Cacheable {}

abstract class BaseService implements Loggable
{
    abstract public function process(): void;
}

class UserService extends BaseService implements Cacheable
{
    public function process(): void {}
}

$refClass = new \ReflectionClass(UserService::class);

echo "父类: " . $refClass->getParentClass()?->getName() . "\n";
echo "是 BaseService 的子类: " . ($refClass->isSubclassOf(BaseService::class) ? 'yes' : 'no') . "\n";
echo "实现 Loggable: " . ($refClass->implementsInterface(Loggable::class) ? 'yes' : 'no') . "\n";
echo "实现 Cacheable: " . ($refClass->implementsInterface(Cacheable::class) ? 'yes' : 'no') . "\n";

echo "\n实现的接口:\n";
foreach ($refClass->getInterfaceNames() as $interface) {
    echo "  {$interface}\n";
}

动态实例化类

php
<?php
declare(strict_types=1);

class Config
{
    public function __construct(
        public readonly string $app = 'MyApp',
        public readonly string $env = 'production',
        public readonly bool $debug = false
    ) {}
}

$refClass = new \ReflectionClass(Config::class);

// 使用 newInstanceArgs 传递参数数组
$instance = $refClass->newInstanceArgs(['TestApp', 'development', true]);

echo $instance->app . "\n"; // TestApp
echo $instance->env . "\n"; // development
echo $instance->debug ? 'true' : 'false'; // true

详细说明

ReflectionClass 常用方法一览

方法说明返回值
getName()完整类名string
getShortName()短类名string
getNamespaceName()命名空间string
getFileName()定义文件路径string|false
getStartLine()起始行号int|false
getEndLine()结束行号int|false
getParentClass()父类反射ReflectionClass|null
getMethods()方法列表ReflectionMethod[]
getProperties()属性列表ReflectionProperty[]
getConstants()常量列表array
getConstructor()构造函数ReflectionMethod|null
getInterfaces()接口列表ReflectionClass[]
getInterfaceNames()接口名列表string[]
getTraits()trait 列表ReflectionClass[]
isSubclassOf()是否为子类bool
implementsInterface()是否实现接口bool
isInstantiable()是否可实例化bool
isAbstract()是否抽象类bool
isFinal()是否 finalbool
newInstanceArgs()创建实例object
hasMethod()是否有指定方法bool
hasProperty()是否有指定属性bool
getMethod()获取指定方法ReflectionMethod
getProperty()获取指定属性ReflectionProperty

ReflectionClass 与接口

php
<?php
declare(strict_types=1);

interface Renderable
{
    public function render(): string;
}

class View implements Renderable
{
    public function render(): string
    {
        return '<div>Hello</div>';
    }
}

$refClass = new \ReflectionClass(View::class);
$interfaces = $refClass->getInterfaces();

foreach ($interfaces as $interface) {
    echo $interface->getName() . "\n";
}
// Renderable

实战示例

实战:简易 DI 容器

php
<?php
declare(strict_types=1);

class Container
{
    /** @var array<string, object> */
    private array $instances = [];
    /** @var array<string, callable> */
    private array $bindings = [];

    public function bind(string $abstract, callable $concrete): void
    {
        $this->bindings[$abstract] = $concrete;
    }

    public function singleton(string $abstract, callable $concrete): void
    {
        $this->bindings[$abstract] = function (self $container) use ($concrete): object {
            if (!isset($this->instances[$abstract])) {
                $this->instances[$abstract] = $concrete($container);
            }
            return $this->instances[$abstract];
        };
    }

    public function get(string $abstract): object
    {
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        if (isset($this->bindings[$abstract])) {
            return ($this->bindings[$abstract])($this);
        }

        return $this->resolve($abstract);
    }

    private function resolve(string $className): object
    {
        $refClass = new \ReflectionClass($className);

        if (!$refClass->isInstantiable()) {
            throw new \RuntimeException("无法实例化: {$className}");
        }

        $constructor = $refClass->getConstructor();
        if ($constructor === null) {
            return $refClass->newInstance();
        }

        $params = $this->resolveParameters($constructor);
        return $refClass->newInstanceArgs($params);
    }

    private function resolveParameters(\ReflectionMethod $constructor): array
    {
        $params = [];
        foreach ($constructor->getParameters() as $param) {
            $type = $param->getType();
            if ($type instanceof \ReflectionNamedType && !$type->isBuiltin()) {
                $params[] = $this->get($type->getName());
            } elseif ($param->isDefaultValueAvailable()) {
                $params[] = $param->getDefaultValue();
            } else {
                throw new \RuntimeException("无法解析参数: {$param->getName()}");
            }
        }
        return $params;
    }
}

注意事项

反射性能

反射操作有一定性能开销,频繁使用时应缓存 ReflectionClass 实例。

内置类的反射

PHP 内置类(如 stdClassPDO)的反射信息有限:getFileName() 等返回 false

匿名类的反射

PHP 7.0+ 的匿名类可以被反射,但类名是动态生成的,包含 \0 字符。

最佳实践

  1. 使用类常量而非字符串ClassName::class 比字符串更安全。
  2. 缓存反射实例:避免重复创建 ReflectionClass
  3. 使用 hasMethod/hasProperty 前置检查:避免直接调用 getMethod/getProperty 抛出异常。
php
<?php
declare(strict_types=1);

class ReflectionCache
{
    /** @var array<string, \ReflectionClass> */
    private static array $cache = [];

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

ReflectionClass 高级用法

动态实例化与工厂模式

php
<?php
declare(strict_types=1);

class ServiceFactory
{
    /** @var array<string, array> */
    private array $registrations = [];

    public function register(string $interface, string $concrete, array $params = []): void
    {
        $this->registrations[$interface] = [
            'concrete' => $concrete,
            'params' => $params,
        ];
    }

    public function create(string $interface): object
    {
        if (!isset($this->registrations[$interface])) {
            throw new \InvalidArgumentException("未注册: {$interface}");
        }

        $concrete = $this->registrations[$interface]['concrete'];
        $params = $this->registrations[$interface]['params'];

        $refClass = new \ReflectionClass($concrete);

        // 检查是否实现了指定接口
        if (!$refClass->implementsInterface($interface)) {
            throw new \RuntimeException("{$concrete} 未实现 {$interface}");
        }

        return $refClass->newInstanceArgs($params);
    }
}

interface CacheInterface { public function get(string $key): mixed; }
class RedisCache implements CacheInterface
{
    public function __construct(private string $host = 'localhost', private int $port = 6379) {}
    public function get(string $key): mixed { return null; }
}

$factory = new ServiceFactory();
$factory->register(CacheInterface::class, RedisCache::class, ['127.0.0.1', 6380]);
$cache = $factory->create(CacheInterface::class);

检查类的继承层次

php
<?php
declare(strict_types=1);

abstract class BaseEntity
{
    protected int $id = 0;
}

abstract class BaseService
{
    protected ?\PDO $db = null;
}

class UserService extends BaseService
{
    // ...
}

class AdminService extends UserService
{
    // ...
}

function analyzeClass(string $className): string
{
    $ref = new \ReflectionClass($className);
    $lines = ["类: {$ref->getName()}"];

    // 获取父类链
    $parent = $ref->getParentClass();
    while ($parent !== false) {
        $lines[] = "  继承自: {$parent->getName()}";
        $parent = $parent->getParentClass();
    }

    // 获取接口列表
    $interfaces = $ref->getInterfaceNames();
    if (!empty($interfaces)) {
        $lines[] = "  实现接口: " . implode(', ', $interfaces);
    }

    // 获取 trait 列表
    $traits = $ref->getTraitNames();
    if (!empty($traits)) {
        $lines[] = "  使用 trait: " . implode(', ', $traits);
    }

    return implode("\n", $lines);
}

echo analyzeClass(AdminService::class) . "\n";

获取类的文件位置信息

php
<?php
declare(strict_types=1);

function getClassInfo(string $className): array
{
    $ref = new \ReflectionClass($className);
    return [
        'name' => $ref->getName(),
        'shortName' => $ref->getShortName(),
        'namespace' => $ref->getNamespaceName(),
        'file' => $ref->getFileName(),
        'startLine' => $ref->getStartLine(),
        'endLine' => $ref->getEndLine(),
        'isInternal' => $ref->isInternal(),
        'isUserDefined' => $ref->isUserDefined(),
    ];
}

print_r(getClassInfo(\ReflectionClass::class));

常见误区与 FAQ

ReflectionClass::newInstance 和 new 有什么区别?

newInstance()newInstanceArgs() 通过反射实例化,可以处理可变数量的参数。new 在编译时确定参数。反射方式更适合动态创建对象。

如何检查一个类是否是抽象类且可实例化?

php
<?php
declare(strict_types=1);

$ref = new \ReflectionClass(SomeClass::class);
if ($ref->isAbstract()) {
    echo "抽象类,不能直接实例化\n";
}
if ($ref->isInstantiable()) {
    $instance = $ref->newInstanceWithoutConstructor();
}

getMethods 是否包含父类的方法?

是的,getMethods() 默认返回类中所有可见方法,包括继承的方法。可以通过 ReflectionClass::getMethods(\ReflectionMethod::IS_PUBLIC) 等过滤器限制。

参考链接