PHP项目反射与注解

wen PHP项目 2

PHP反射与注解详解

反射(Reflection)

什么是反射

反射是PHP在运行时获取类、方法、属性、参数等结构信息的机制。

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支持 有限 原生支持,代码补全

注意事项

  1. 性能影响:反射操作相对较慢,建议缓存结果
  2. 版本要求:Attributes需要PHP 8.0+版本
  3. 测试覆盖:使用反射的代码需要充分的单元测试
  4. 可维护性:避免过度使用,保持代码简洁性
  5. 错误处理:确保反射操作有适当的错误处理机制

反射和注解是现代PHP框架的基础,合理使用可以构建灵活、可扩展的应用架构。

抱歉,评论功能暂时关闭!