stubbles/reflect

反射辅助函数和注解。

v10.0.0 2023-12-31 11:48 UTC

README

反射辅助函数和注解。

构建状态

Tests

Latest Stable Version Latest Unstable Version

安装

stubbles/reflectComposer 包的形式分发。要将它作为您包的依赖项安装,请使用以下命令

composer require "stubbles/reflect": "^10.0"

需求

stubbles/reflect 至少需要 PHP 8.2。

此外,它使用 stubbles/sequence 从某些函数返回序列,并使用 stubbles/values 从注解中解析值。

反射辅助函数

所有函数都在命名空间 stubbles\reflect 中。

reflect()

自 3.1.0 版本起可用

为了提供更多便利,提供了一个函数 stubbles\reflect\reflect()。它允许反射类、对象和方法

$refClass  = reflect('some\interesting\UserDefinedClass'); // creates instance of \ReflectionClass
$refObject = reflect($someObjectInstance); // creates instance of \ReflectionObject
$refMethod = reflect('some\interesting\UserDefinedClass', 'aMethod'); // creates instance of \ReflectionMethod
$refMethod = reflect($someObjectInstance, 'aMethod'); // same as line before

自 4.0.0 版本起,它还允许反射函数

$refFunction = reflect('someFunction'); // creates instance of \ReflectionFunction

reflectConstructor(): \ReflectionMethod

reflect($someObject, '__construct') 的快捷方式

$refMethod =  reflectConstructor('some\interesting\UserDefinedClass'); // same as reflect('some\interesting\UserDefinedClass', '__construct');
$refMethod =  reflectConstructor($someObjectInstance); // same as reflect('some\interesting\UserDefinedClass', '__construct');

methodsOf($class, $filter = null): \stubbles\sequence\Sequence

返回给定类的所有方法的序列。


propertiesOf($class, $filter = null): \stubbles\sequence\Sequence

返回给定类的所有属性的序列。


parametersOf($classOrFunction, $methodName = null): \stubbles\sequence\Sequence

返回给定类的所有属性的序列。


parametersOfConstructor($class): \stubbles\sequence\Sequence

parametersOf($classOrFunction, '__construct') 的快捷方式。

parameter($name, $classOrFunction, $methodName = null): \ReflectionParameter

从引用的函数或方法中返回具有给定名称的参数。


constructorParameter($name, $class): \ReflectionParameter

parameter($name, $class, '__construct') 的快捷方式。

注解

有关注解背后的概念的详细信息,请参阅 注解,其中包含对该主题的通用介绍。有关如何在编程语言中使用此内容的更多详细信息,请参阅 Java 注解

如何定义注解

注解可以定义在可以获取 docblock 注释的每个元素上:函数、方法、类和属性。此外,还可以在包含参数的函数或方法的 docblock 注释中定义注解。

namespace my;
/**
 * Class to demonstrate how to define annotations
 *
 * @MyAnnotation
 */
class ExampleClass
{
    /**
     * an example property
     *
     * @var  string
     * @AnnotationWithValues(bar='dummy', baz=42, required=true)
     */
    protected $bar;

    /**
     * another example property
     *
     * @var  string
     * @AnnotationWithOneValue("anotherDummy")
     */
    protected $baz;

    /**
     * an example method
     *
     * @param  int  $param  a parameter
     * @CastedAnnotation[MyAnnotation]
     * @ParamAnnotation{param}(key='value')
     */
    public function aMethod($param)
    {
        // some code here
    }
}

在上面的例子中,你可以看到定义注解的五种不同方式。然而,你可以根据自己的喜好组合这些方式。你可能有一个没有、一个或多个值的 casted 注解。但让我们来回顾一下所有定义的注解。

  • @MyAnnotation 这是一个没有值的注解。
  • @AnnotationWithValues(bar='dummy', baz=42, required=true) 这个注解有两个值,参数 bar、baz 和 required。一个是字符串,另一个是整数,最后一个是一个布尔值。
  • @AnnotationWithOneValue("anotherDummy") 这个注解只有一个字符串值。
  • @CastedAnnotation[MyAnnotation] casted 注解可以用来区分标记和提示注解消费者类应执行的具体操作。第一个名称是标记,方括号中的名称表示要执行的操作。
  • @ParamAnnotation{param}(key='value') 这个注解是为方法 aMethod() 的参数 $param 定义的参数注解。花括号中的名称表示此注解所针对的参数的名称。请注意,当通过 annotationsOf('Foo', 'aMethod') 获取它时,此注解不可用,这将导致 ReflectionException

与其他实现不同,不需要为注解创建一个单独的类。

读取注解

要获取可注解元素的所有注解列表,请调用 stubbles\reflect\annotationsOf() 函数。它返回一个 stubbles\reflect\annotation\Annotations 实例,并支持以下调用

annotationsOf('my\ExampleClass', 'aMethod'); // returns annotations of this method
annotationsOf($exampleInstance, 'aMethod'); // returns annotations of this method
annotationsOf('my\ExampleClass'); // returns annotations of this class
annotationsOf($exampleInstance); // returns annotations of this class
annotationsOf('my\examplefunction'); // returns annotations of this function
annotationsOf($reflectionParameter); // returns annotations of this parameter
annotationsOf($reflectionProperty); // returns annotations of this class property

作为检索类构造函数注解的便捷函数,可以使用 stubbles\reflect\annotationsOfConstructorParameter()

annotationsOfConstructorParameter('my\ExampleClass');
annotationsOfConstructorParameter($exampleInstance);

作为检索函数或方法参数注解的便捷函数,可以使用 'stubbles\reflect\annotationsOfParameter()`

annotationsOfParameter('param', 'my\ExampleClass', 'aMethod');
annotationsOfParameter('param', $exampleInstance, 'aMethod');
annotationsOfParameter('param', 'someFunction');

从注解中读取值

如上所示,注解可以有值。如果我们从上面的类中取出 @AnnotationWithValues,可以这样访问它们

$annotation = annotationsOf(/** reflectable annotation source */);
echo $annotation->getBar(); // prints "dummy"
echo $annotation->bar; // prints "dummy"
echo $annotation->getValueByName('bar'); // prints "dummy"
if ($annotation->isRequired()) {
    echo 'Required!';
}

这些是可能的方法

  • 可以使用方法 is 后跟其名称来检索布尔值。
  • 其他任何值类型都可以使用方法 get 后跟其名称来检索,作为注解类的属性,或使用 getValueByName() 方法。 请注意,布尔值也可以使用 get 方法的语法来检索,但大多数情况下看起来并不好:getRequired()isRequired() 在代码阅读者的观点上具有不同的含义。

如果注解只有一个无名的值,可以使用 $annotation->value$annotation->getValue() 来检索。

使用 get 方法语法的好处是可以提供一个默认值,如果未设置具有此名称的值,则将返回该默认值

echo $annotation->getAwesomeness('Roland TB-303'); // prints "Roland TB-303"

is 方法语法的默认值是 false

如果值是可选的,可以检查其存在性:$annotation->hasValueByName('bar') 如果设置了具有名称 bar 的值,则返回 true,否则返回 false

读取参数值时,它们将使用 stubbles/valuesstubbles\values\Parse 进行解析。请参阅此包的文档了解如何解析值。

注解缓存

由于解析注解相当昂贵,Stubbles 会在读取后缓存注解。然而,默认情况下,缓存仅在请求级别上工作,并且不是持久的。为了在不同请求之间持久化注解缓存,应用程序需要提供注解缓存持久化。

这样做最简单的方法是调用 stubbles\reflect\annotation\persistAnnotationsInFile('/path/to/some/cachefile.cache'),它将使用参数中指定的文件名来持久化注解缓存。

如果缓存文件不满足您的需求,您还可以提供不同的持久化。为此,您需要调用 stubbles\reflect\annotation\persistAnnotations() 并提供两个函数,一个函数可以读取数据并返回它,另一个函数可以接收数据并将其存储。对于文件缓存实现,它将如下所示

persistAnnotations(
        function() use($cacheFile)
        {
            if (file_exists($cacheFile)) {
                return unserialize(file_get_contents($cacheFile));
            }

            return [];
        },
        function(array $annotationData) use($cacheFile)
        {
            file_put_contents($cacheFile, serialize($annotationData));
        }
);

第一个函数必须返回存储的注解数据。如果不存在此类数据,它必须返回一个空数组。

第二个函数必须存储传入的注解数据。

请注意,如果您使用持久化注解缓存,您需要能够清除此缓存,因为代码中对注解的更改不会导致缓存数据的更新。这可能导致困惑,为什么更改后的注解在代码执行时不会被尊重。