sun/staticreflection

用于在发现之后场景下的静态 PHP 类代码反射。

1.0.0 2014-06-26 13:08 UTC

This package is auto-updated.

Last update: 2024-08-29 03:29:33 UTC


README

用于在发现之后场景下的静态 PHP 类代码反射。

这个 PHP 框架的实用库允许在已知文件系统位置的情况下,不将 PHP 类代码加载到内存中,来反射文件的头部。

静态反射对于过滤之前发现的类文件列表,例如接口或基类等常见方面非常有用。

ReflectionClass 提供与原生 \ReflectionClass 相同的 API。

原生 PHP 反射 容易失控,因为它不仅加载反射的类,还加载所有依赖类和接口。PHP 代码无法卸载。高内存消耗可能导致应用程序超过 PHP 的内存限制。— 静态反射避免将每个反射类的所有依赖和祖先类自动加载到内存中。

在最坏/理想的使用案例中,你只生成一个 可用 类的列表,而不立即使用它们(例如,用于用户选择或可交换的插件实现)。

示例 xhprof 差分结果

1,538 个候选类,其中 180 个接口、特性、抽象和其他辅助类被过滤掉

使用示例

  1. 先决条件:某些发现产生了一个类映射

    {
      "Sun\StaticReflection\ReflectionClass":
        "./src/ReflectionClass.php",
      "Sun\Tests\StaticReflection\ReflectionClassTest":
        "./tests/src/ReflectionClassTest.php",
      "Sun\Tests\StaticReflection\Fixtures\Example":
        "./tests/fixtures/Example.php",
      "Sun\Tests\StaticReflection\Fixtures\Base\ImportedInterface":
        "./tests/fixtures/Base/ImportedInterface.php"
      ...
    }

    → 你有一个 classname => pathname 映射。

  2. 过滤所有发现的类文件

    use Sun\StaticReflection\ReflectionClass;
    
    $list = array();
    foreach ($classmap as $classname => $pathname) {
      $class = new ReflectionClass($classname, $pathname);
    
      // Only include tests.
      if (!$class->isSubclassOf('PHPUnit_Framework_TestCase')) {
        continue;
      }
    
      // …optionally prepare them for a listing/later selection:
      $doc_comment = $class->getDocComment();
      $list[$classname] = array(
        'summary' => $doc_comment->getSummary(),
        'covers' => $doc_comment->getAnnotations()['coversDefaultClass'][0],
      );
    }
    echo json_encode($list, JSON_PRETTY_PRINT);
    {
      "Sun\Tests\StaticReflection\ReflectionClassTest": {
        "summary": "Tests ReflectionClass.",
        "covers": "\Sun\StaticReflection\ReflectionClass"
      }
    }

    → 你过滤了可用类的列表,而没有将所有代码加载到内存中。

  3. 这为什么重要

    array_walk($classmap, function (&$pathname, $classname) {
      $pathname = class_exists($classname, FALSE) || interface_exists($classname, FALSE);
    });
    echo json_encode($classmap, JSON_PRETTY_PRINT);
    {
      "Sun\Tests\StaticReflection\ReflectionClassTest": false,
      "Sun\Tests\StaticReflection\Fixtures\Example": false,
      "Sun\Tests\StaticReflection\Fixtures\ExampleInterface": true,
      "Sun\Tests\StaticReflection\Fixtures\Base\Example": true,
      ...
    }

    → 仅加载了每个类/接口的 祖先。本身静态反射的类没有加载。

  4. ProTip™ - ReflectionClass::isSubclassOfAny()

    要过滤一组常见的父类/接口,首先检查静态反射信息。只有在需要进一步检查的情况下才继续使用 isSubclassOf();例如。

    // Static reflection.
    if (!$class->isSubclassOfAny(array('Condition\FirstFlavor', 'Condition\SecondFlavor'))) {
      continue;
    }
    // Native reflection of ancestors (if the reflected class has any).
    if (!$class->isSubclassOf('Condition\BaseFlavor')) {
      continue;
    }

要求

  • PHP 5.4.2+

限制

  1. 每个文件只能有一个类/接口/特性(PSR-2,PSR-0/PSR-4),且必须定义在文件中 第一个

  2. implementsInterface($interface) 返回 TRUE,即使 $interface 是一个类。

  3. \ReflectionClass::IS_IMPLICIT_ABSTRACT 不受支持,因为方法没有被分析。(只分析文件头部)

  4. \ReflectionClass::$name 是只读的,因此不可用。请使用 getName() 代替。

  5. 调用任何其他未实现的 \ReflectionClass 方法会导致致命错误。

    \ReflectionClass 类可能会在需要时按需懒加载(PRs 欢迎)。ReflectionClass 已实现了所有技术上可以支持的方法。

注意

  • StaticReflection 可以绕过删除注释的字节码缓存。

灵感

静态/反射

PHPDoc 标签/注释

许可证

MIT — 版权(c)2014 丹尼尔·F·库迪恩(sun)