PHP反射与注解详解
反射(Reflection)
什么是反射
反射是PHP在运行时获取类、方法、属性、参数等结构信息的机制。

核心反射类
<?php
// 示例类
class User {
private int $id;
public string $name;
protected string $email;
public function __construct(string $name, string $email) {
$this->name = $name;
$this->email = $email;
}
public function getName(): string {
return $this->name;
}
private function validateEmail(): bool {
return filter_var($this->email, FILTER_VALIDATE_EMAIL) !== false;
}
}
// 获取反射对象
$reflectionClass = new ReflectionClass(User::class);
// 获取类信息
echo "类名: " . $reflectionClass->getName() . "\n";
echo "父类: " . ($reflectionClass->getParentClass()?->getName() ?? '无') . "\n";
echo "是否抽象: " . ($reflectionClass->isAbstract() ? '是' : '否') . "\n";
// 获取属性
$properties = $reflectionClass->getProperties();
foreach ($properties as $property) {
echo "属性: {$property->getName()} ({$property->getModifiers()})\n";
}
// 获取方法
$methods = $reflectionClass->getMethods();
foreach ($methods as $method) {
echo "方法: {$method->getName()} (访问修饰符: {$method->getModifiers()})\n";
}
反射的实际应用
<?php
// 依赖注入容器(简化版)
class Container {
private array $instances = [];
public function get(string $className) {
if (!isset($this->instances[$className])) {
$this->instances[$className] = $this->resolve($className);
}
return $this->instances[$className];
}
private function resolve(string $className) {
$reflectionClass = new ReflectionClass($className);
// 检查是否可实例化
if (!$reflectionClass->isInstantiable()) {
throw new Exception("Class {$className} cannot be instantiated");
}
// 获取构造函数
$constructor = $reflectionClass->getConstructor();
if ($constructor === null) {
return new $className();
}
// 解析构造函数参数
$parameters = $constructor->getParameters();
$dependencies = [];
foreach ($parameters as $parameter) {
$type = $parameter->getType();
if ($type === null || $type->isBuiltin()) {
if ($parameter->isDefaultValueAvailable()) {
$dependencies[] = $parameter->getDefaultValue();
} else {
throw new Exception("Cannot resolve parameter: {$parameter->getName()}");
}
} else {
$dependencies[] = $this->get($type->getName());
}
}
return $reflectionClass->newInstanceArgs($dependencies);
}
}
// 使用示例
class Database {
public function query(string $sql) {
return "Executing: {$sql}";
}
}
class UserRepository {
private Database $database;
public function __construct(Database $database) {
$this->database = $database;
}
public function findAll() {
return $this->database->query("SELECT * FROM users");
}
}
$container = new Container();
$userRepo = $container->get(UserRepository::class);
echo $userRepo->findAll();
注解(Attributes)- PHP 8+
定义注解
<?php
// 定义注解类
#[Attribute]
class Route {
public function __construct(
public string $path,
public string $method = 'GET'
) {}
}
#[Attribute]
class Middleware {
public function __construct(
public string $name,
public array $options = []
) {}
}
#[Attribute]
class Validation {
public function __construct(
public array $rules = []
) {}
}
使用注解
<?php
// 使用注解
#[Route('/users', 'GET')]
#[Middleware('auth')]
#[Middleware('log')]
class UserController {
#[Route('/users/{id}', 'GET')]
#[Validation(['id' => 'required|numeric'])]
public function show(int $id): array {
return ['user' => ['id' => $id, 'name' => 'John']];
}
#[Route('/users', 'POST')]
#[Validation(['name' => 'required|string', 'email' => 'required|email'])]
public function store(array $data): array {
return ['status' => 'created'];
}
}
读取注解
<?php
// 路由处理器
class RouteHandler {
private array $routes = [];
public function registerControllers(string ...$controllers): void {
foreach ($controllers as $controller) {
$this->registerController($controller);
}
}
private function registerController(string $controllerClass): void {
$reflectionClass = new ReflectionClass($controllerClass);
// 读取类注解
$classAttributes = $reflectionClass->getAttributes();
$classMiddleware = [];
foreach ($classAttributes as $attribute) {
if ($attribute->getName() === Middleware::class) {
$middleware = $attribute->newInstance();
$classMiddleware[] = $middleware->name;
}
}
// 读取方法注解
foreach ($reflectionClass->getMethods() as $method) {
$methodAttributes = $method->getAttributes(Route::class);
foreach ($methodAttributes as $attribute) {
$route = $attribute->newInstance();
$this->routes[$route->method][$route->path] = [
'controller' => $controllerClass,
'method' => $method->getName(),
'middleware' => $classMiddleware,
'validation' => $this->getValidationRules($method)
];
}
}
}
private function getValidationRules(ReflectionMethod $method): array {
$attributes = $method->getAttributes(Validation::class);
if (empty($attributes)) {
return [];
}
$validation = $attributes[0]->newInstance();
return $validation->rules;
}
public function dispatch(string $method, string $path): void {
$path = rtrim($path, '/');
foreach ($this->routes[$method] ?? [] as $routePath => $routeInfo) {
// 简单的路径匹配(实际项目应使用正则)
if ($this->matchPath($routePath, $path)) {
$this->executeRoute($routeInfo, $path);
return;
}
}
throw new Exception("Route not found: {$method} {$path}");
}
private function matchPath(string $pattern, string $path): bool {
// 简化的路径匹配
$pattern = preg_replace('/\{([a-zA-Z_]+)\}/', '([^/]+)', $pattern);
return preg_match("#^{$pattern}$#", $path) === 1;
}
private function executeRoute(array $routeInfo, string $path): void {
$controllerClass = $routeInfo['controller'];
$methodName = $routeInfo['method'];
// 执行中间件
foreach ($routeInfo['middleware'] as $middleware) {
echo "执行中间件: {$middleware}\n";
}
// 执行控制器方法
$controller = new $controllerClass();
$controller->$methodName(1);
}
}
// 使用示例
$handler = new RouteHandler();
$handler->registerControllers(UserController::class);
try {
$handler->dispatch('GET', '/users/123');
echo "\n\n";
$handler->dispatch('POST', '/users');
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
综合应用:ORM框架示例
<?php
// 定义ORM相关注解
#[Attribute]
class Table {
public function __construct(
public string $name
) {}
}
#[Attribute]
class Column {
public function __construct(
public string $name,
public string $type = 'string',
public bool $nullable = false
) {}
}
#[Attribute]
class Id {
public function __construct(
public bool $autoIncrement = true
) {}
}
#[Attribute]
class OneToMany {
public function __construct(
public string $targetEntity,
public string $mappedBy
) {}
}
// 实体类
#[Table('users')]
class User {
#[Id]
#[Column('id', 'integer')]
private int $id;
#[Column('username', 'string')]
private string $username;
#[Column('email', 'string')]
private string $email;
#[Column('created_at', 'datetime')]
private ?DateTime $createdAt = null;
#[OneToMany(Post::class, 'user_id')]
private array $posts = [];
}
// ORM映射器
class EntityMapper {
private array $entityMap = [];
public function registerEntity(string $entityClass): void {
$reflectionClass = new ReflectionClass($entityClass);
// 获取表名
$tableAttributes = $reflectionClass->getAttributes(Table::class);
if (empty($tableAttributes)) {
throw new Exception("Entity {$entityClass} must have Table attribute");
}
$table = $tableAttributes[0]->newInstance();
$tableName = $table->name;
// 解析列映射
$columns = [];
$relations = [];
foreach ($reflectionClass->getProperties() as $property) {
$this->parseProperty($property, $columns, $relations);
}
$this->entityMap[$entityClass] = [
'table' => $tableName,
'columns' => $columns,
'relations' => $relations
];
}
private function parseProperty(ReflectionProperty $property, array &$columns, array &$relations): void {
$attributes = $property->getAttributes();
foreach ($attributes as $attribute) {
$instance = $attribute->newInstance();
if ($instance instanceof Column) {
$columns[$property->getName()] = [
'name' => $instance->name,
'type' => $instance->type,
'nullable' => $instance->nullable
];
} elseif ($instance instanceof OneToMany) {
$relations[$property->getName()] = [
'type' => 'oneToMany',
'targetEntity' => $instance->targetEntity,
'mappedBy' => $instance->mappedBy
];
}
}
}
public function getTableName(string $entityClass): string {
return $this->entityMap[$entityClass]['table'] ?? '';
}
public function getColumns(string $entityClass): array {
return $this->entityMap[$entityClass]['columns'] ?? [];
}
}
// 使用示例
$mapper = new EntityMapper();
$mapper->registerEntity(User::class);
echo "Table: " . $mapper->getTableName(User::class) . "\n";
echo "Columns:\n";
print_r($mapper->getColumns(User::class));
最佳实践
性能优化
// 缓存反射结果
class ReflectionCache {
private static array $cache = [];
public static function getClass(string $className): ReflectionClass {
if (!isset(self::$cache[$className])) {
self::$cache[$className] = new ReflectionClass($className);
}
return self::$cache[$className];
}
public static function getAttributes(string $className, string $attributeName): array {
$reflectionClass = self::getClass($className);
return $reflectionClass->getAttributes($attributeName);
}
}
特性比较
| 特性 | 旧版PHP注解 | PHP 8+ Attributes |
|---|---|---|
| 语法 | 通过文档注释解析 | 原生支持,明确语法 |
| 性能 | 需要解析字符串 | 直接读取,性能更好 |
| 类型安全 | 无类型检查 | 有明确的类型定义 |
| IDE支持 | 有限 | 原生支持,代码补全 |
注意事项
- 性能影响:反射操作相对较慢,建议缓存结果
- 版本要求:Attributes需要PHP 8.0+版本
- 测试覆盖:使用反射的代码需要充分的单元测试
- 可维护性:避免过度使用,保持代码简洁性
- 错误处理:确保反射操作有适当的错误处理机制
反射和注解是现代PHP框架的基础,合理使用可以构建灵活、可扩展的应用架构。