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() | 是否 final | bool |
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 内置类(如 stdClass、PDO)的反射信息有限:getFileName() 等返回 false。
匿名类的反射
PHP 7.0+ 的匿名类可以被反射,但类名是动态生成的,包含 \0 字符。
最佳实践
- 使用类常量而非字符串:
ClassName::class比字符串更安全。 - 缓存反射实例:避免重复创建
ReflectionClass。 - 使用 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) 等过滤器限制。