diff --git a/CHANGELOG.md b/CHANGELOG.md index f10b165..483c260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ Yii Framework 2 apidoc extension Change Log - Bug #338: Fix deprecation error `Using null as an array offset is deprecated, use an empty string instead` (mspirkov) - Enh #337: Log invalid tags (mspirkov) -- Enh #339: Add support for PHPStan/Psalm syntax (mspirkov) +- Enh #339, #340: Add support for PHPStan/Psalm syntax (mspirkov) - Enh #339: Add support for intersection types (mspirkov) - Bug #339: Fix the mechanism for replacing `static` with FQCN (mspirkov) - Bug #339: Fix processing of multidimensional arrays (mspirkov) diff --git a/composer.json b/composer.json index 181f5f5..f380060 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "yiisoft/yii2": "~2.0.16", "yiisoft/yii2-bootstrap": "~2.0.0", "phpdocumentor/reflection": "^5.3.0 || ^6.0.0", - "phpdocumentor/type-resolver": "^1.11", + "phpdocumentor/type-resolver": "^1.12", "nikic/php-parser": "^4.0 || ^5.0", "cebe/js-search": "~0.9.0", "cebe/markdown": "^1.0", diff --git a/models/BaseDoc.php b/models/BaseDoc.php index 7c19f9a..5f2988b 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -8,6 +8,7 @@ namespace yii\apidoc\models; +use InvalidArgumentException; use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Tags\Deprecated; use phpDocumentor\Reflection\DocBlock\Tags\Generic; @@ -22,6 +23,7 @@ use phpDocumentor\Reflection\Php\Property; use phpDocumentor\Reflection\Php\Trait_; use phpDocumentor\Reflection\TypeResolver; +use RuntimeException; use yii\base\BaseObject; use yii\helpers\StringHelper; @@ -242,51 +244,63 @@ public function __construct($parent = null, $reflector = null, $context = null, $this->templates[(string) $fqsen] = $tag; unset($this->tags[$i]); } elseif ($tag instanceof Generic) { - if ($tag->getName() === self::TODO_TAG_NAME) { - $this->todos[] = $tag; - unset($this->tags[$i]); - } elseif ($tag->getName() === self::PHPSTAN_TYPE_ANNOTATION_NAME) { - $tagData = explode(' ', trim($tag->getDescription()), 2); - $phpStanType = new PseudoTypeDoc( - PseudoTypeDoc::TYPE_PHPSTAN, - $this, - trim($tagData[0]), - $typeResolver->resolve(trim($tagData[1]), $this->phpDocContext) - ); - $fqsen = $fqsenResolver->resolve($phpStanType->name, $this->phpDocContext); - $this->phpStanTypes[(string) $fqsen] = $phpStanType; - unset($this->tags[$i]); - } elseif ($tag->getName() === self::PSALM_TYPE_ANNOTATION_NAME) { - $tagData = explode('=', trim($tag->getDescription()), 2); - $psalmType = new PseudoTypeDoc( - PseudoTypeDoc::TYPE_PSALM, - $this, - trim($tagData[0]), - $typeResolver->resolve(trim($tagData[1]), $this->phpDocContext) - ); - $fqsen = $fqsenResolver->resolve($psalmType->name, $this->phpDocContext); - $this->psalmTypes[(string) $fqsen] = $psalmType; - unset($this->tags[$i]); - } elseif ($tag->getName() === self::PHPSTAN_IMPORT_TYPE_ANNOTATION_NAME) { - $tagData = explode(' from ', trim($tag->getDescription()), 2); - $phpStanTypeImport = new PseudoTypeImportDoc( - PseudoTypeImportDoc::TYPE_PHPSTAN, - trim($tagData[0]), - $fqsenResolver->resolve(trim($tagData[1]), $this->phpDocContext) - ); - $fqsen = $fqsenResolver->resolve($phpStanTypeImport->typeName, $this->phpDocContext); - $this->phpStanTypeImports[(string) $fqsen] = $phpStanTypeImport; - unset($this->tags[$i]); - } elseif ($tag->getName() === self::PSALM_IMPORT_TYPE_ANNOTATION_NAME) { - $tagData = explode(' from ', trim($tag->getDescription()), 2); - $psalmTypeImport = new PseudoTypeImportDoc( - PseudoTypeImportDoc::TYPE_PSALM, - trim($tagData[0]), - $fqsenResolver->resolve(trim($tagData[1]), $this->phpDocContext) - ); - $fqsen = $fqsenResolver->resolve($psalmTypeImport->typeName, $this->phpDocContext); - $this->psalmTypeImports[(string) $fqsen] = $psalmTypeImport; - unset($this->tags[$i]); + try { + if ($tag->getName() === self::TODO_TAG_NAME) { + $this->todos[] = $tag; + unset($this->tags[$i]); + } elseif ($tag->getName() === self::PHPSTAN_TYPE_ANNOTATION_NAME) { + $tagData = explode(' ', trim($tag->getDescription()), 2); + $phpStanType = new PseudoTypeDoc( + PseudoTypeDoc::TYPE_PHPSTAN, + $this, + trim($tagData[0]), + $typeResolver->resolve(trim($tagData[1]), $this->phpDocContext) + ); + $fqsen = $fqsenResolver->resolve($phpStanType->name, $this->phpDocContext); + $this->phpStanTypes[(string) $fqsen] = $phpStanType; + unset($this->tags[$i]); + } elseif ($tag->getName() === self::PSALM_TYPE_ANNOTATION_NAME) { + $tagData = explode('=', trim($tag->getDescription()), 2); + $psalmType = new PseudoTypeDoc( + PseudoTypeDoc::TYPE_PSALM, + $this, + trim($tagData[0]), + $typeResolver->resolve(trim($tagData[1]), $this->phpDocContext) + ); + $fqsen = $fqsenResolver->resolve($psalmType->name, $this->phpDocContext); + $this->psalmTypes[(string) $fqsen] = $psalmType; + unset($this->tags[$i]); + } elseif ($tag->getName() === self::PHPSTAN_IMPORT_TYPE_ANNOTATION_NAME) { + $tagData = explode(' from ', trim($tag->getDescription()), 2); + $phpStanTypeImport = new PseudoTypeImportDoc( + PseudoTypeImportDoc::TYPE_PHPSTAN, + trim($tagData[0]), + $fqsenResolver->resolve(trim($tagData[1]), $this->phpDocContext) + ); + $fqsen = $fqsenResolver->resolve($phpStanTypeImport->typeName, $this->phpDocContext); + $this->phpStanTypeImports[(string) $fqsen] = $phpStanTypeImport; + unset($this->tags[$i]); + } elseif ($tag->getName() === self::PSALM_IMPORT_TYPE_ANNOTATION_NAME) { + $tagData = explode(' from ', trim($tag->getDescription()), 2); + $psalmTypeImport = new PseudoTypeImportDoc( + PseudoTypeImportDoc::TYPE_PSALM, + trim($tagData[0]), + $fqsenResolver->resolve(trim($tagData[1]), $this->phpDocContext) + ); + $fqsen = $fqsenResolver->resolve($psalmTypeImport->typeName, $this->phpDocContext); + $this->psalmTypeImports[(string) $fqsen] = $psalmTypeImport; + unset($this->tags[$i]); + } + } catch (InvalidArgumentException | RuntimeException $e) { + if ($context !== null){ + $context->errors[] = [ + 'line' => $this->startLine, + 'file' => $this->sourceFile, + 'message' => 'Exception: ' . $e->getMessage(), + ]; + } else { + throw $e; + } } } elseif ($tag instanceof InvalidTag && $context !== null) { $exception = $tag->getException(); diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__18.txt b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__18.txt index da7445e..838e1d6 100644 --- a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__18.txt +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__18.txt @@ -233,91 +233,91 @@ Array [33] => Array ( - [line] => 32 + [line] => 34 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getThreeStringsArray' ) [34] => Array ( - [line] => 40 + [line] => 42 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'testOffsetAccess' ) [35] => Array ( - [line] => 48 + [line] => 50 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getNonEmptyList' ) [36] => Array ( - [line] => 56 + [line] => 58 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getListWithoutGenerics' ) [37] => Array ( - [line] => 64 + [line] => 66 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getNonEmptyListWithoutGenerics' ) [38] => Array ( - [line] => 72 + [line] => 74 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getClassWithTwoGenerics' ) [39] => Array ( - [line] => 79 + [line] => 81 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'methodWithInvalidReturnTag' ) [40] => Array ( - [line] => 86 + [line] => 88 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getArrayOfStatic' ) [41] => Array ( - [line] => 93 + [line] => 95 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getArrayWithStaticGeneric' ) [42] => Array ( - [line] => 100 + [line] => 102 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getIterableWithStaticGeneric' ) [43] => Array ( - [line] => 107 + [line] => 109 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getArrayShapeWithStaticGeneric' ) [44] => Array ( - [line] => 114 + [line] => 116 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getObjectShapeWithStaticGeneric' ) [45] => Array ( - [line] => 121 + [line] => 123 [file] => /tests/data/api/animal/Dog.php [message] => No short description for Method 'getStaticOrNull' ) diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__19.txt b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__19.txt index f089874..3625a45 100644 --- a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__19.txt +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__19.txt @@ -30,7 +30,14 @@ Array [4] => Array ( - [line] => 79 + [line] => 20 + [file] => /tests/data/api/animal/Dog.php + [message] => Exception: "\yiiunit\apidoc\data\api\animal\invalid-type" is not a valid Fqsen. + ) + + [5] => Array + ( + [line] => 81 [file] => /tests/data/api/animal/Dog.php [message] => Invalid tag: @return invalid-type. Exception message: "\yiiunit\apidoc\data\api\animal\invalid-type" is not a valid Fqsen. ) diff --git a/tests/data/api/animal/Dog.php b/tests/data/api/animal/Dog.php index 7a59430..bb43cb2 100644 --- a/tests/data/api/animal/Dog.php +++ b/tests/data/api/animal/Dog.php @@ -12,6 +12,8 @@ * * @phpstan-type MyArray array{foo: int, bar: string} * + * @phpstan-type InvalidType invalid-type + * * @author Paul Klimov * @since 1.1 */