如何在PHP中编写自定义注解来修改类?以@Getters生成getter为例
在PHP中实现@Getters注解自动生成Getter方法
PHP本身没有原生注解系统,但可以通过解析类的文档注释(DocComment)结合反射机制,实现自动生成Getter方法的@Getters注解。下面分两种常见方案实现:
方案一:使用魔术方法动态处理Getter调用
这种方式无需修改类定义,通过__call魔术方法拦截不存在的Getter调用,直接返回对应私有属性的值。
1. 编写注解处理器类
class AnnotationProcessor { public static function instantiate($className, ...$args) { $reflection = new ReflectionClass($className); // 检查类文档注释是否包含@Getters $docComment = $reflection->getDocComment(); if (strpos($docComment, '@Getters') === false) { return $reflection->newInstanceArgs($args); } // 创建继承原类的匿名类,通过__call处理Getter return new class(...$args) extends $className { public function __call($methodName, $arguments) { // 匹配get开头的方法名 if (str_starts_with($methodName, 'get')) { $propertyName = lcfirst(substr($methodName, 3)); if (property_exists($this, $propertyName)) { return $this->$propertyName; } } throw new BadMethodCallException("方法 {$methodName} 不存在"); } }; } }
2. 使用@Getters注解并实例化类
/** * @Getters */ class Person { private $name; private $age; public function __construct($name, $age) { $this->name = $name; $this->age = $age; } } // 通过处理器实例化 $person = AnnotationProcessor::instantiate(Person::class, 'Alice', 30); echo $person->getName(); // 输出 Alice echo $person->getAge(); // 输出 30
方案二:类加载时预生成Getter方法
这种方式在类加载阶段动态修改类代码,生成真实的Getter方法,性能比魔术方法更好。
1. 注册自定义自动加载器
spl_autoload_register(function ($className) { $filePath = __DIR__ . '/' . str_replace('\\', '/', $className) . '.php'; if (!file_exists($filePath)) return; $classContent = file_get_contents($filePath); // 检查类是否包含@Getters注解 if (strpos($classContent, '@Getters') === false) { require $filePath; return; } // 提取类名和私有属性 preg_match('/class (\w+)/', $classContent, $classMatch); $className = $classMatch[1]; preg_match_all('/private \$(\w+);/', $classContent, $propertyMatches); $properties = $propertyMatches[1]; // 生成Getter方法代码 $getterCode = ''; foreach ($properties as $prop) { $methodName = 'get' . ucfirst($prop); $getterCode .= " public function {$methodName}() { return \$this->{$prop}; } "; } // 将Getter插入类定义 $modifiedContent = preg_replace("/class {$className} {/", "class {$className} {{$getterCode}", $classContent); // 临时文件加载修改后的类 $tempFile = tempnam(sys_get_temp_dir(), 'php_getter_'); file_put_contents($tempFile, $modifiedContent); require $tempFile; unlink($tempFile); });
2. 直接使用带@Getters注解的类
/** * @Getters */ class Person { private $name; private $age; public function __construct($name, $age) { $this->name = $name; $this->age = $age; } } // 直接实例化,自动拥有Getter方法 $person = new Person('Bob', 25); echo $person->getName(); // 输出 Bob echo $person->getAge(); // 输出 25
注意事项
- 注解必须写在类的文档注释块(
/** ... */)中,单行注释无法被解析。 - 方案一实现简单,但每次调用Getter都会触发
__call,性能略低;方案二生成真实的方法,性能更优,但需要处理类加载逻辑。 - 如果需要复杂注解功能,可考虑使用成熟的第三方库,但简单场景下自己实现足够。
内容的提问来源于stack exchange,提问作者Mike U
相关产品推荐
相关产品推荐

