uuf6429/phpstan-phpdoc-type-resolver

从PHPStan的PHPDoc解析器中解析(完全限定)类型

3.0.0 2024-08-04 12:52 UTC

This package is auto-updated.

Last update: 2024-09-15 16:10:38 UTC


README

CI codecov Minimum PHP Version License Latest Stable Version Latest Unstable Version

从PHPStan的PHPDoc解析器中解析(完全限定)类型。

💾 安装

可以使用 Composer 安装此包,只需运行以下命令

composer require uuf6429/phpstan-phpdoc-type-resolver

如果打算仅在开发期间使用此库,请考虑使用 --dev

🤔 为什么?

因为 phpstan/phpdoc-parser 不解析类型(这不是它的责任)并且 phpdocument/type-resolver 目前有一些重大限制

🚀 使用方法

原则上,解析器需要以下两点

  1. PHPStan-PHPDoc 类型(TypeNode 的实例)。
  2. 该类型出现位置的 'Scope' 信息。

有两种方式可以检索这些信息,如下所示。

重要:解析器将始终将一些特定的 PHPStan 类型转换为以下类型

(*) 转换是强制性的,失败将触发某种异常(意味着:原始类型 不应 返回)。

😎 通过反射

假设我们有一个 \My\Project\Greeter 类,它有一个 greet 方法,以下是解析该方法的返回类型的方法

<?php

// Reflect our class method
$reflector = new \ReflectionMethod(\My\Project\Greeter::class, 'greet');

// Use the provided factory to easily parse the PHPDoc, which additionally automatically resolves the types
$docBlock = \uuf6429\PHPStanPHPDocTypeResolver\PhpDoc\Factory::createInstance()
    ->createFromReflector($reflector);

// And finally, retrieve the resolved type of the return tag
/** @var \PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode $returnTag */
$returnTag = $docBlock->getTag('@return');
$finalReturnType = $returnTag->type;

🙈 无工厂/DocBlock 包装器

假设我们有一个 \My\Project\Greeter 类,它有一个 greet 方法,以下是解析该方法的返回类型的更长的途径

<?php

// Reflect our class method
$reflector = new \ReflectionMethod(\My\Project\Greeter::class, 'greet');

// Use the scope resolver to get information about that method
$scopeResolver = new \uuf6429\PHPStanPHPDocTypeResolver\PhpDoc\ReflectorScopeResolver();
$scope = $scopeResolver->resolve($reflector);

// Parse the PHPDoc block with PHPStan PHPDoc parser
$lexer = new \PHPStan\PhpDocParser\Lexer\Lexer();
$constExprParser = new \PHPStan\PhpDocParser\Parser\ConstExprParser();
$typeParser = new \PHPStan\PhpDocParser\Parser\TypeParser($constExprParser);
$parser = new \PHPStan\PhpDocParser\Parser\PhpDocParser($typeParser, $constExprParser);
$docBlock = $parser->parse(
    new \PHPStan\PhpDocParser\Parser\TokenIterator(
        $lexer->tokenize($scope->comment)   // 👈 note that the scope resolver also retrieves the PHPDoc block for us
    )
);

// Finally, we initialize the type resolver and resolve the first return type of the doc block
$typeResolver = new \uuf6429\PHPStanPHPDocTypeResolver\TypeResolver();
$finalReturnType = $typeResolver->resolve($scope, $docBlock->getReturnTagValues()[0]->type);

🤪 通过源字符串

也可以在不实际加载PHP源代码的情况下解析类型(这是反射的要求)。然而,这将需要更多的工作 - 主要区别在于您需要自行设置作用域。

假设我们想解析PHP源代码字符串中的类型

<?php

$source = <<<'PHP'
<?php

namespace My\Project\Services;

use My\Project\PersonEntity as Person;

class Greeter {
    /**
     * @param Person|object{name: string} $person
     */
    public function greet($person): void {
        echo "Hello, {$person->name}!";
    }
}

PHP;

// Construct the scope manually - automating this will take some work
$scope = new \uuf6429\PHPStanPHPDocTypeResolver\PhpDoc\Scope(
    // In-memory file; you could also use php memory streams etc
    file: 'data:base64,' . base64_encode($source),
    // approximate line where the type has occurred - everything else below has to be specified manually
    line: 73,
    class: 'My\Project\Services\Greeter',
    comment: <<<'PHP'
        /**
         * @param Person|object{name: string} $person
         */
        PHP
);

// The factory can also be used with a custom scope
$docBlock = \uuf6429\PHPStanPHPDocTypeResolver\PhpDoc\Factory::createInstance()
    ->createFromScope($scope);

// And as before, retrieve the resolved type of the return tag
/** @var \PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode $returnTag */
$returnTag = $docBlock->getTag('@return');
$finalReturnType = $returnTag->type;