diff --git a/CHANGELOG.md b/CHANGELOG.md index d7848bf..f10b165 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,15 @@ Yii Framework 2 apidoc extension Change Log =========================================== -3.0.9 under development +4.0.0 under development ----------------------- - 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: 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) 3.0.8 November 24, 2025 diff --git a/commands/GuideController.php b/commands/GuideController.php index 538138e..b972355 100644 --- a/commands/GuideController.php +++ b/commands/GuideController.php @@ -13,7 +13,6 @@ use yii\apidoc\renderers\GuideRenderer; use yii\helpers\Console; use yii\helpers\FileHelper; -use Yii; use yii\helpers\Json; /** diff --git a/composer.json b/composer.json index 3f82582..181f5f5 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,8 @@ "php": "^7.4 || ^8.0", "yiisoft/yii2": "~2.0.16", "yiisoft/yii2-bootstrap": "~2.0.0", - "phpdocumentor/reflection": "^5.1.0 || ^6.0.0", + "phpdocumentor/reflection": "^5.3.0 || ^6.0.0", + "phpdocumentor/type-resolver": "^1.11", "nikic/php-parser": "^4.0 || ^5.0", "cebe/js-search": "~0.9.0", "cebe/markdown": "^1.0", diff --git a/helpers/TypeHelper.php b/helpers/TypeHelper.php index 6ac50e4..db2e665 100644 --- a/helpers/TypeHelper.php +++ b/helpers/TypeHelper.php @@ -7,6 +7,8 @@ namespace yii\apidoc\helpers; +use phpDocumentor\Reflection\PseudoTypes\Conditional; +use phpDocumentor\Reflection\PseudoTypes\ConditionalForParameter; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\Types\AggregatedType; @@ -18,24 +20,41 @@ final class TypeHelper { /** - * @return string[] + * @return Type[] */ - public static function splitType(?Type $type): array + public static function getTypesByAggregatedType(AggregatedType $compound): array { - if ($type === null) { - return []; + $types = []; + foreach ($compound as $type) { + $types[] = $type; } - // TODO: Don't split the Intersection - if (!$type instanceof AggregatedType) { - return [(string) $type]; - } + return $types; + } + /** + * @param Conditional|ConditionalForParameter $type + * @return Type[] Possible unique types. + */ + public static function getPossibleTypesByConditionalType(Type $type): array + { $types = []; - foreach ($type as $childType) { - $types[] = (string) $childType; + + foreach ([$type->getIf(), $type->getElse()] as $innerType) { + if ($innerType instanceof Conditional || $innerType instanceof ConditionalForParameter) { + $types = array_merge($types, self::getPossibleTypesByConditionalType($innerType)); + } elseif ($innerType instanceof AggregatedType) { + $types = array_merge($types, self::getTypesByAggregatedType($innerType)); + } else { + $types[] = $innerType; + } } - return $types; + $uniqueTypes = []; + foreach ($types as $innerType) { + $uniqueTypes[(string) $innerType] = $innerType; + } + + return array_values($uniqueTypes); } } diff --git a/models/BaseDoc.php b/models/BaseDoc.php index 5be9abd..7c19f9a 100644 --- a/models/BaseDoc.php +++ b/models/BaseDoc.php @@ -13,12 +13,15 @@ use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag; use phpDocumentor\Reflection\DocBlock\Tags\Since; +use phpDocumentor\Reflection\DocBlock\Tags\Template; +use phpDocumentor\Reflection\FqsenResolver; use phpDocumentor\Reflection\Php\Class_; use phpDocumentor\Reflection\Php\Constant; use phpDocumentor\Reflection\Php\Interface_; use phpDocumentor\Reflection\Php\Method; use phpDocumentor\Reflection\Php\Property; use phpDocumentor\Reflection\Php\Trait_; +use phpDocumentor\Reflection\TypeResolver; use yii\base\BaseObject; use yii\helpers\StringHelper; @@ -32,6 +35,12 @@ */ class BaseDoc extends BaseObject { + private const PHPSTAN_TYPE_ANNOTATION_NAME = 'phpstan-type'; + private const PSALM_TYPE_ANNOTATION_NAME = 'psalm-type'; + + private const PHPSTAN_IMPORT_TYPE_ANNOTATION_NAME = 'phpstan-import-type'; + private const PSALM_IMPORT_TYPE_ANNOTATION_NAME = 'psalm-import-type'; + private const INHERITDOC_TAG_NAME = 'inheritdoc'; private const TODO_TAG_NAME = 'todo'; @@ -39,7 +48,13 @@ class BaseDoc extends BaseObject * @var \phpDocumentor\Reflection\Types\Context|null */ public $phpDocContext; + /** + * @var string|null + */ public $name; + /** + * @var string|null + */ public $fullName; public $sourceFile; public $startLine; @@ -64,6 +79,30 @@ class BaseDoc extends BaseObject * @var Generic[] */ public $todos = []; + /** + * @var array + */ + public $templates = []; + /** + * @var self|null + */ + public $parent = null; + /** + * @var array + */ + public array $phpStanTypes = []; + /** + * @var array + */ + public array $psalmTypes = []; + /** + * @var array + */ + public array $phpStanTypeImports = []; + /** + * @var array + */ + public array $psalmTypeImports = []; /** * Checks if doc has tag of a given name @@ -126,18 +165,24 @@ public function getPackageName() } /** + * @param self|null $parent * @param Class_|Method|Trait_|Interface_|Property|Constant|null $reflector * @param Context|null $context * @param array $config */ - public function __construct($reflector = null, $context = null, $config = []) + public function __construct($parent = null, $reflector = null, $context = null, $config = []) { parent::__construct($config); + $this->parent = $parent; + if ($reflector === null) { return; } + $fqsenResolver = new FqsenResolver(); + $typeResolver = new TypeResolver($fqsenResolver); + // base properties $this->fullName = trim((string) $reflector->getFqsen(), '\\()'); @@ -160,7 +205,7 @@ public function __construct($reflector = null, $context = null, $config = []) return; } - $this->shortDescription = StringHelper::mb_ucfirst($docBlock->getSummary());; + $this->shortDescription = StringHelper::mb_ucfirst($docBlock->getSummary()); if (empty($this->shortDescription) && !($this instanceof PropertyDoc) && $context !== null && !$docBlock->getTagsByName(self::INHERITDOC_TAG_NAME)) { $context->warnings[] = [ 'line' => $this->startLine, @@ -192,9 +237,57 @@ public function __construct($reflector = null, $context = null, $config = []) $this->deprecatedSince = $tag->getVersion(); $this->deprecatedReason = (string) $tag->getDescription(); unset($this->tags[$i]); - } elseif ($tag instanceof Generic && $tag->getName() === self::TODO_TAG_NAME) { - $this->todos[] = $tag; + } elseif ($tag instanceof Template) { + $fqsen = $fqsenResolver->resolve($tag->getTemplateName(), $this->phpDocContext); + $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]); + } } elseif ($tag instanceof InvalidTag && $context !== null) { $exception = $tag->getException(); $message = 'Invalid tag: ' . $tag->render() . '.'; diff --git a/models/ClassDoc.php b/models/ClassDoc.php index 9bda7a0..3b33c3f 100644 --- a/models/ClassDoc.php +++ b/models/ClassDoc.php @@ -7,6 +7,8 @@ namespace yii\apidoc\models; +use phpDocumentor\Reflection\Php\Class_; + /** * Represents API documentation information for a `class`. * @@ -91,7 +93,9 @@ public function getNativeEvents() } /** - * @inheritdoc + * @param Class_|null $reflector + * @param Context|null $context + * @param array $config */ public function __construct($reflector = null, $context = null, $config = []) { @@ -118,11 +122,11 @@ public function __construct($reflector = null, $context = null, $config = []) foreach ($reflector->getConstants() as $constantReflector) { $docBlock = $constantReflector->getDocBlock(); if ($docBlock !== null && count($docBlock->getTagsByName('event')) > 0) { - $event = new EventDoc($constantReflector, null, [], $docBlock); + $event = new EventDoc($this, $constantReflector, null, [], $docBlock); $event->definedBy = $this->name; $this->events[$event->name] = $event; } else { - $constant = new ConstDoc($constantReflector); + $constant = new ConstDoc($this, $constantReflector); $constant->definedBy = $this->name; $this->constants[$constant->name] = $constant; } diff --git a/models/ConstDoc.php b/models/ConstDoc.php index 845b7e8..3e4e1c2 100644 --- a/models/ConstDoc.php +++ b/models/ConstDoc.php @@ -18,6 +18,9 @@ */ class ConstDoc extends BaseDoc { + /** + * @var string|null + */ public $definedBy; /** * @var string|null @@ -26,14 +29,15 @@ class ConstDoc extends BaseDoc /** + * @param ClassDoc|TraitDoc $parent * @param Constant|null $reflector * @param Context|null $context * @param array $config * @param DocBlock|null $docBlock */ - public function __construct($reflector = null, $context = null, $config = [], $docBlock = null) + public function __construct($parent, $reflector = null, $context = null, $config = [], $docBlock = null) { - parent::__construct($reflector, $context, $config); + parent::__construct($parent, $reflector, $context, $config); if ($reflector === null) { return; diff --git a/models/Context.php b/models/Context.php index f59a6fc..dbff87d 100644 --- a/models/Context.php +++ b/models/Context.php @@ -1,4 +1,5 @@ files[$fileName] = sha1_file($fileName); } + /** + * @param File $reflection + * @param string $fileName + */ private function parseFile($reflection, $fileName) { foreach ($reflection->getClasses() as $class) { @@ -292,7 +297,7 @@ protected function inheritDocs($class) } // set all properties that are empty. - foreach (['shortDescription', 'type', 'types', 'since'] as $property) { + foreach (['shortDescription', 'type', 'since'] as $property) { if (empty($p->$property) || is_string($p->$property) && trim($p->$property) === '') { // only copy @since if the package names are equal (or missing) if ($property === 'since' && $p->getPackageName() !== $inheritedProperty->getPackageName()) { @@ -325,7 +330,7 @@ protected function inheritDocs($class) continue; } // set all properties that are empty. - foreach (['shortDescription', 'return', 'returnType', 'returnTypes', 'exceptions', 'since'] as $property) { + foreach (['shortDescription', 'return', 'returnType', 'exceptions', 'since'] as $property) { if (empty($m->$property) || is_string($m->$property) && trim($m->$property) === '') { // only copy @since if the package names are equal (or missing) if ($property === 'since' && $m->getPackageName() !== $inheritedMethod->getPackageName()) { @@ -353,12 +358,9 @@ protected function inheritDocs($class) if (empty($param->description) || trim($param->description) === '') { $param->description = $inheritedMethod->params[$i]->description; } - if (empty($param->type) || trim($param->type) === '') { + if ($param->type === null) { $param->type = $inheritedMethod->params[$i]->type; } - if (empty($param->types)) { - $param->types = $inheritedMethod->params[$i]->types; - } } $m->removeTag('inheritdoc'); } @@ -378,7 +380,7 @@ private function inheritMethodRecursive($method, $class) ); $methods = []; - foreach($inheritanceCandidates as $candidate) { + foreach ($inheritanceCandidates as $candidate) { if (isset($candidate->methods[$method->name])) { $cmethod = $candidate->methods[$method->name]; if (!$candidate instanceof InterfaceDoc && $cmethod->hasTag('inheritdoc')) { @@ -404,7 +406,7 @@ private function inheritPropertyRecursive($method, $class) ); $properties = []; - foreach($inheritanceCandidates as $candidate) { + foreach ($inheritanceCandidates as $candidate) { if (isset($candidate->properties[$method->name])) { $cproperty = $candidate->properties[$method->name]; if ($cproperty->hasTag('inheritdoc')) { @@ -436,7 +438,7 @@ private function getParents($class) private function getInterfaces($class) { $interfaces = []; - foreach($class->interfaces as $interface) { + foreach ($class->interfaces as $interface) { if (isset($this->interfaces[$interface])) { $interfaces[] = $this->interfaces[$interface]; } @@ -468,7 +470,7 @@ protected function handlePropertyFeature($class) ]; } else { // Override the setter-defined property if it exists already - $class->properties[$propertyName] = new PropertyDoc(null, $this, [ + $class->properties[$propertyName] = new PropertyDoc($class, null, $this, [ 'name' => $propertyName, 'fullName' => "$class->name::$propertyName", 'definedBy' => $method->definedBy, @@ -476,7 +478,6 @@ protected function handlePropertyFeature($class) 'visibility' => 'public', 'isStatic' => false, 'type' => $method->returnType, - 'types' => $method->returnTypes, 'shortDescription' => BaseDoc::extractFirstSentence($method->return), 'description' => $method->return, 'since' => $method->since, @@ -502,7 +503,7 @@ protected function handlePropertyFeature($class) } } else { $param = $this->getFirstNotOptionalParameter($method); - $class->properties[$propertyName] = new PropertyDoc(null, $this, [ + $class->properties[$propertyName] = new PropertyDoc($class, null, $this, [ 'name' => $propertyName, 'fullName' => "$class->name::$propertyName", 'definedBy' => $method->definedBy, @@ -510,7 +511,6 @@ protected function handlePropertyFeature($class) 'visibility' => 'public', 'isStatic' => false, 'type' => $param->type, - 'types' => $param->types, 'shortDescription' => BaseDoc::extractFirstSentence($param->description), 'description' => $param->description, 'since' => $method->since, @@ -574,6 +574,9 @@ protected function isSubclassOf($classA, $classB) return false; } + /** + * @return Project + */ public function getReflectionProject() { $files = []; @@ -588,6 +591,9 @@ public function getReflectionProject() $projectFactory->addStrategy(new ClassConstantFactory($docBlockFactory, new PrettyPrinter()), $priority); $projectFactory->addStrategy(new PropertyFactory($docBlockFactory, new PrettyPrinter()), $priority); - return $projectFactory->create('ApiDoc', $files); + /** @var Project */ + $project = $projectFactory->create('ApiDoc', $files); + + return $project; } } diff --git a/models/EventDoc.php b/models/EventDoc.php index 6b68115..721e6b8 100644 --- a/models/EventDoc.php +++ b/models/EventDoc.php @@ -11,6 +11,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\Php\Class_; use phpDocumentor\Reflection\Php\Constant; +use phpDocumentor\Reflection\Types\Object_; use yii\helpers\StringHelper; /** @@ -25,21 +26,18 @@ class EventDoc extends ConstDoc * @var string|null */ public $type; - /** - * @var string[]|null - */ - public $types; /** + * @param ClassDoc|TraitDoc $parent * @param Class_|Constant|null $reflector * @param Context|null $context * @param array $config * @param DocBlock|null $docBlock */ - public function __construct($reflector = null, $context = null, $config = [], $docBlock = null) + public function __construct($parent, $reflector = null, $context = null, $config = [], $docBlock = null) { - parent::__construct($reflector, $context, $config); + parent::__construct($parent, $reflector, $context, $config); if ($reflector === null) { return; @@ -62,7 +60,6 @@ public function __construct($reflector = null, $context = null, $config = [], $d $this->type = $docBlock->getContext()->getNamespace() . '\\' . $className; } - $this->types = [$this->type]; $this->shortDescription = BaseDoc::extractFirstSentence($this->description); unset($this->tags[$i]); } diff --git a/models/FunctionDoc.php b/models/FunctionDoc.php index dfb0fbb..47e996b 100644 --- a/models/FunctionDoc.php +++ b/models/FunctionDoc.php @@ -1,4 +1,5 @@ isReturnByReference = $reflector->getHasReturnByReference(); foreach ($reflector->getArguments() as $arg) { - $arg = new ParamDoc($arg, $context, ['sourceFile' => $this->sourceFile]); + $arg = new ParamDoc($this, $arg, $context, ['sourceFile' => $this->sourceFile]); $this->params[$arg->name] = $arg; } @@ -63,7 +70,7 @@ public function __construct($reflector = null, $context = null, $config = []) foreach ($this->tags as $i => $tag) { if ($tag instanceof Throws) { - $this->exceptions[implode(TypeHelper::splitType($tag->getType()))] = $tag->getDescription(); + $this->exceptions[] = $tag; unset($this->tags[$i]); } elseif ($tag instanceof Param) { $paramName = '$' . $tag->getVariableName(); @@ -75,13 +82,12 @@ public function __construct($reflector = null, $context = null, $config = []) ]; continue; } + $this->params[$paramName]->description = StringHelper::mb_ucfirst($tag->getDescription()); - $this->params[$paramName]->type = (string) $tag->getType(); - $this->params[$paramName]->types = TypeHelper::splitType($tag->getType()); + $this->params[$paramName]->type = $tag->getType(); unset($this->tags[$i]); } elseif ($tag instanceof Return_) { - $this->returnType = (string) $tag->getType(); - $this->returnTypes = TypeHelper::splitType($tag->getType()); + $this->returnType = $tag->getType(); $this->return = StringHelper::mb_ucfirst($tag->getDescription()); unset($this->tags[$i]); } elseif ($this->isInheritdocTag($tag)) { @@ -90,8 +96,7 @@ public function __construct($reflector = null, $context = null, $config = []) } if (!$hasInheritdoc && $this->returnType === null) { - $this->returnType = (string) $reflector->getReturnType(); - $this->returnTypes = [$this->returnType]; + $this->returnType = $reflector->getReturnType(); } } } diff --git a/models/MethodDoc.php b/models/MethodDoc.php index 8997575..e1598fb 100644 --- a/models/MethodDoc.php +++ b/models/MethodDoc.php @@ -22,7 +22,9 @@ class MethodDoc extends FunctionDoc public $isFinal; public $isStatic; public $visibility; - // will be set by creating class + /** + * @var string|null + */ public $definedBy; /** * @var string @@ -31,13 +33,14 @@ class MethodDoc extends FunctionDoc /** + * @param TypeDoc $parent * @param Class_|Method|null $reflector * @param Context|null $context * @param array $config */ - public function __construct($reflector = null, $context = null, $config = []) + public function __construct($parent, $reflector = null, $context = null, $config = []) { - parent::__construct($reflector, $context, $config); + parent::__construct($parent, $reflector, $context, $config); if ($reflector === null) { return; diff --git a/models/ParamDoc.php b/models/ParamDoc.php index f631747..0e3bae7 100644 --- a/models/ParamDoc.php +++ b/models/ParamDoc.php @@ -8,7 +8,7 @@ namespace yii\apidoc\models; use phpDocumentor\Reflection\Php\Argument; -use yii\apidoc\helpers\TypeHelper; +use phpDocumentor\Reflection\Type; use yii\base\BaseObject; /** @@ -29,31 +29,32 @@ class ParamDoc extends BaseObject // will be set by creating class public $description; /** - * @var string|null + * @var Type|null */ public $type; + public $sourceFile; /** - * @var string[]|null + * @var FunctionDoc|MethodDoc */ - public $types; - public $sourceFile; - + public $parent; /** + * @param FunctionDoc|MethodDoc $parent * @param Argument|null $reflector * @param Context|null $context * @param array $config */ - public function __construct($reflector = null, $context = null, $config = []) + public function __construct($parent, $reflector = null, $context = null, $config = []) { parent::__construct($config); + $this->parent = $parent; + if ($reflector !== null) { $this->name = $reflector->getName(); if ($this->type === null) { - $this->type = (string) $reflector->getType(); - $this->types = TypeHelper::splitType($reflector->getType()); + $this->type = $reflector->getType(); } if (PHP_VERSION_ID >= 80100) { diff --git a/models/PropertyDoc.php b/models/PropertyDoc.php index a8a9b75..5b52411 100644 --- a/models/PropertyDoc.php +++ b/models/PropertyDoc.php @@ -9,7 +9,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Var_; use phpDocumentor\Reflection\Php\Property; -use yii\apidoc\helpers\TypeHelper; +use phpDocumentor\Reflection\Type; use yii\helpers\StringHelper; /** @@ -26,13 +26,9 @@ class PropertyDoc extends BaseDoc public $visibility; public $isStatic; /** - * @var string|null + * @var Type|null */ public $type; - /** - * @var string[]|null - */ - public $types; /** * @var string|null */ @@ -40,7 +36,9 @@ class PropertyDoc extends BaseDoc // will be set by creating class public $getter; public $setter; - // will be set by creating class + /** + * @var string|null + */ public $definedBy; @@ -61,13 +59,14 @@ public function getIsWriteOnly() } /** + * @param TypeDoc $parent * @param Property|null $reflector * @param Context|null $context * @param array $config */ - public function __construct($reflector = null, $context = null, $config = []) + public function __construct($parent, $reflector = null, $context = null, $config = []) { - parent::__construct($reflector, $context, $config); + parent::__construct($parent, $reflector, $context, $config); if ($reflector === null) { return; @@ -86,9 +85,7 @@ public function __construct($reflector = null, $context = null, $config = []) $hasInheritdoc = false; foreach ($this->tags as $tag) { if ($tag instanceof Var_) { - $this->type = (string) $tag->getType(); - $this->types = TypeHelper::splitType($tag->getType()); - + $this->type = $tag->getType(); $this->description = StringHelper::mb_ucfirst($tag->getDescription()); $this->shortDescription = BaseDoc::extractFirstSentence($this->description); } elseif ($this->isInheritdocTag($tag)) { @@ -105,8 +102,7 @@ public function __construct($reflector = null, $context = null, $config = []) } if (!$hasInheritdoc && $this->type === null) { - $this->type = (string) $reflector->getType(); - $this->types = [$this->type]; + $this->type = $reflector->getType(); } } } diff --git a/models/PseudoTypeDoc.php b/models/PseudoTypeDoc.php new file mode 100644 index 0000000..61ab865 --- /dev/null +++ b/models/PseudoTypeDoc.php @@ -0,0 +1,53 @@ + + * @immutable + */ +final class PseudoTypeDoc extends BaseObject +{ + public const TYPE_PHPSTAN = 'phpstan'; + public const TYPE_PSALM = 'psalm'; + + public const TYPES = [ + self::TYPE_PHPSTAN, + self::TYPE_PSALM, + ]; + + /** @var value-of */ + public string $type; + + public BaseDoc $parent; + + public string $name; + + public Type $value; + + /** + * @param value-of $type + */ + public function __construct( + string $type, + BaseDoc $parent, + string $name, + Type $value + ) { + $this->type = $type; + $this->parent = $parent; + $this->name = $name; + $this->value = $value; + } +} diff --git a/models/PseudoTypeImportDoc.php b/models/PseudoTypeImportDoc.php new file mode 100644 index 0000000..c0649fd --- /dev/null +++ b/models/PseudoTypeImportDoc.php @@ -0,0 +1,49 @@ + + * @immutable + */ +final class PseudoTypeImportDoc extends BaseObject +{ + public const TYPE_PHPSTAN = 'phpstan'; + public const TYPE_PSALM = 'psalm'; + + public const TYPES = [ + self::TYPE_PHPSTAN, + self::TYPE_PSALM, + ]; + + /** @var value-of */ + public string $type; + + public string $typeName; + + public Fqsen $typeParentFqsen; + + /** + * @param value-of $type + */ + public function __construct( + string $type, + string $typeName, + Fqsen $typeParentFqsen + ) { + $this->type = $type; + $this->typeName = $typeName; + $this->typeParentFqsen = $typeParentFqsen; + } +} diff --git a/models/TypeDoc.php b/models/TypeDoc.php index 7ccea4d..066a62d 100644 --- a/models/TypeDoc.php +++ b/models/TypeDoc.php @@ -1,4 +1,5 @@ namespace = trim(StringHelper::dirname($this->name), '\\'); @@ -204,18 +205,17 @@ public function __construct($reflector = null, $context = null, $config = []) } if ($tag instanceof Property || $tag instanceof PropertyRead || $tag instanceof PropertyWrite) { - $shortDescription = $tag->getDescription() ? BaseDoc::extractFirstSentence($tag->getDescription()): ''; + $shortDescription = $tag->getDescription() ? BaseDoc::extractFirstSentence($tag->getDescription()) : ''; $name = '$' . $tag->getVariableName(); - $property = new PropertyDoc(null, $context, [ + $property = new PropertyDoc($this, null, $context, [ 'sourceFile' => $this->sourceFile, 'name' => $name, 'fullName' => ltrim((string) $reflector->getFqsen(), '\\') . '::' . $name, 'isStatic' => false, 'visibility' => 'public', 'definedBy' => $this->name, - 'type' => (string) $tag->getType(), - 'types' => TypeHelper::splitType($tag->getType()), + 'type' => $tag->getType(), 'shortDescription' => $shortDescription, 'description' => $tag->getDescription(), ]); @@ -224,35 +224,30 @@ public function __construct($reflector = null, $context = null, $config = []) } if ($tag instanceof Method) { - $params = []; - - foreach ($tag->getParameters() as $parameter) { - $argumentType = $parameter->getType(); - - $params[] = new ParamDoc(null, $context, [ - 'sourceFile' => $this->sourceFile, - 'name' => $parameter->getName(), - 'type' => (string) $argumentType, - 'types' => TypeHelper::splitType($argumentType), - ]); - } - $shortDescription = $tag->getDescription() ? BaseDoc::extractFirstSentence($tag->getDescription()) : ''; $description = $shortDescription ? substr($tag->getDescription(), strlen($shortDescription)) : ''; - $method = new MethodDoc(null, $context, [ + $method = new MethodDoc($this, null, $context, [ 'sourceFile' => $this->sourceFile, 'name' => $tag->getMethodName(), 'fullName' => ltrim((string) $reflector->getFqsen(), '\\') . '::' . $tag->getMethodName(), 'shortDescription' => $shortDescription, 'description' => $description, 'visibility' => 'public', - 'params' => $params, + 'params' => [], 'isStatic' => $tag->isStatic(), 'return' => ' ', - 'returnType' => (string) $tag->getReturnType(), - 'returnTypes' => TypeHelper::splitType($tag->getReturnType()), + 'returnType' => $tag->getReturnType(), ]); + + foreach ($tag->getParameters() as $parameter) { + $method->params[] = new ParamDoc($method, null, $context, [ + 'sourceFile' => $this->sourceFile, + 'name' => $parameter->getName(), + 'type' => $parameter->getType(), + ]); + } + $method->definedBy = $this->name; $this->methods[$method->name] = $method; } @@ -266,7 +261,7 @@ public function __construct($reflector = null, $context = null, $config = []) } if ((string) $methodReflector->getVisibility() !== 'private') { - $method = new MethodDoc($methodReflector, $context, ['sourceFile' => $this->sourceFile]); + $method = new MethodDoc($this, $methodReflector, $context, ['sourceFile' => $this->sourceFile]); $method->definedBy = $this->name; $this->methods[$method->name] = $method; } @@ -285,7 +280,7 @@ protected function initProperties($reflector, $context) } if ((string) $propertyReflector->getVisibility() !== 'private') { - $property = new PropertyDoc($propertyReflector, $context, ['sourceFile' => $this->sourceFile]); + $property = new PropertyDoc($this, $propertyReflector, $context, ['sourceFile' => $this->sourceFile]); $property->definedBy = $this->name; $this->properties[$property->name] = $property; } diff --git a/renderers/BaseRenderer.php b/renderers/BaseRenderer.php index 5735a2b..b3aa3e9 100644 --- a/renderers/BaseRenderer.php +++ b/renderers/BaseRenderer.php @@ -1,4 +1,5 @@ + */ + private const PHPSTAN_TYPES_DOC_LINKS = [ + 'general-arrays' => [ + 'array', + 'non-empty-array', + ], + 'lists' => [ + 'list', + 'non-empty-list', + ], + 'basic-types' => [ + 'array-key', + 'double', + 'number', + 'scalar', + ], + 'class-string' => [ + 'class-string', + ], + 'other-advanced-string-types' => [ + 'callable-string', + 'numeric-string', + 'non-empty-string', + 'non-falsy-string', + 'truthy-string', + 'literal-string', + 'lowercase-string', + ], + 'integer-ranges' => [ + 'int', + 'positive-int', + 'negative-int', + 'non-positive-int', + 'non-negative-int', + 'non-zero-int', + ], + 'integer-masks' => [ + 'int-mask', + 'int-mask-of', + ], + 'bottom-type' => [ + 'never-return', + 'never-returns', + 'no-return', + ], + 'key-and-value-types-of-arrays-and-iterables' => [ + 'key-of', + 'value-of', + ], + ]; + + /** + * @var array */ - private $phpTypeAliases = [ + private const PHP_TYPE_ALIASES = [ 'true' => 'boolean', 'false' => 'boolean', 'bool' => 'boolean', 'int' => 'integer', ]; + /** - * @var string[] + * @var array */ - private $phpTypeDisplayAliases = [ + private const PHP_TYPE_DISPLAY_ALIASES = [ 'bool' => 'boolean', 'int' => 'integer', ]; + public $guidePrefix = 'guide-'; + public $apiUrl; + /** + * @var string string to use as the title of the generated page. + */ + public $pageTitle; + /** + * @var Context the [[Context]] currently being rendered. + */ + public $apiContext; + /** + * @var Controller the apidoc controller instance. Can be used to control output. + */ + public $controller; + public $guideUrl; public function init() { @@ -100,112 +184,170 @@ public function init() /** * creates a link to a type (class, interface or trait) - * @param ClassDoc|InterfaceDoc|TraitDoc|ClassDoc[]|InterfaceDoc[]|TraitDoc[]|string|string[] $types + * @param BaseDoc|BaseDoc[]|Type|Type[]|string|string[]|null $types * @param BaseDoc|null $context - * @param string $title a title to be used for the link TODO check whether [[yii\...|Class]] is supported + * @param string|null $title a title to be used for the link TODO check whether [[yii\...|Class]] is supported * @param array $options additional HTML attributes for the link. * @return string */ - public function createTypeLink($types, $context = null, $title = null, $options = []) - { + public function createTypeLink( + $types, + ?BaseDoc $context = null, + ?string $title = null, + array $options = [], + ?TypeDoc $currentTypeDoc = null + ) { + if ($types === null) { + return ''; + } + if (!is_array($types)) { $types = [$types]; - } - if (count($types) > 1) { + } elseif (count($types) > 1) { $title = null; } + $links = []; foreach ($types as $type) { - $postfix = ''; if (is_string($type)) { - if (!empty($type) && substr_compare($type, '[]', -2, 2) === 0) { - $postfix = '[]'; - $type = substr($type, 0, -2); + if ($type !== '') { + $typeDoc = $this->getTypeDocByQualifiedClassName($type, $context); + if ($typeDoc !== null) { + $links[] = $this->createTypeLink($typeDoc, $context, $title, $options); + continue; + } + } + } elseif ($type instanceof Type) { + if ($type instanceof Compound) { + $innerTypes = TypeHelper::getTypesByAggregatedType($type); + $links[] = $this->createTypeLink($innerTypes, $context, $title, $options, $currentTypeDoc); + continue; } - if ($type === '$this' && $context instanceof TypeDoc) { - $title = '$this'; - $type = $context; - } elseif (($t = $this->apiContext->getType(ltrim($type, '\\'))) !== null) { - $type = $t; - } elseif (!empty($type) && $type[0] !== '\\' && ($t = $this->apiContext->getType($this->resolveNamespace($context) . '\\' . ltrim($type, '\\'))) !== null) { - $type = $t; + if ($type instanceof ConditionalForParameter || $type instanceof Conditional) { + $possibleTypes = TypeHelper::getPossibleTypesByConditionalType($type); + $links[] = $this->createTypeLink($possibleTypes, $context, $title, $options, $currentTypeDoc); + continue; } - } - if (is_object($type) && method_exists($type, '__toString')) { - $type = (string) $type; - } + if ($type instanceof Intersection) { + $innerTypes = TypeHelper::getTypesByAggregatedType($type); + $innerTypesLinks = array_map( + fn(Type $innerType) => $this->createTypeLink($innerType, $context, $title, $options, $currentTypeDoc), + $innerTypes + ); + $links[] = implode('&', $innerTypesLinks); + continue; + } - if (is_string($type)) { - $linkText = ltrim($type, '\\'); - if ($title !== null) { - $linkText = $title; - $title = null; + if ($type instanceof OffsetAccess) { + $typeLink = $this->createTypeLink($type->getType(), $context, $title, $options); + $links[] = $typeLink . '[' . $type->getOffset() . ']'; + continue; } - // check if it is PHP internal class - if (((class_exists($type, false) || interface_exists($type, false) || trait_exists($type, false)) && - ($reflection = new \ReflectionClass($type)) && $reflection->isInternal())) { - $links[] = $this->generateLink($linkText, 'https://www.php.net/class.' . strtolower(ltrim($type, '\\')), $options) . $postfix; - } elseif (in_array($type, $this->phpTypes)) { - if (isset($this->phpTypeDisplayAliases[$type])) { - $linkText = $this->phpTypeDisplayAliases[$type]; - } - if (isset($this->phpTypeAliases[$type])) { - $type = $this->phpTypeAliases[$type]; + + if ($type instanceof Array_ && substr((string) $type, -3, 3) === ')[]') { + $arrayTypesLinks = $this->createTypeLink($type->getValueType(), $context, $title, $options, $currentTypeDoc); + $links[] = "({$arrayTypesLinks})[]"; + continue; + } + + if ($type instanceof Array_ && substr((string) $type, -2, 2) === '[]') { + $valueType = $type->getValueType(); + if ($valueType instanceof Object_ && $valueType->getFqsen() !== null) { + $templateType = $this->getTemplateType((string) $valueType->getFqsen(), $context); + if ($templateType !== null) { + $typeLink = $this->createTypeLink($templateType, $context, $title, $options, $currentTypeDoc); + $links[] = $templateType instanceof Compound ? "({$typeLink})[]" : "{$typeLink}[]"; + continue; + } } - $links[] = $this->generateLink($linkText, 'https://www.php.net/language.types.' . strtolower(ltrim($type, '\\')), $options) . $postfix; - } else { - $links[] = $type . $postfix; + + $links[] = $this->createTypeLink($valueType, $context, $title, $options, $currentTypeDoc) . '[]'; + continue; + } + + if ($type instanceof ArrayShape) { + $itemsLinks = $this->createLinksByShapeItems($type->getItems(), $context, $title, $options, $currentTypeDoc); + $mainTypeLink = $this->generateLink('array', self::PHPSTAN_TYPE_BASE_URL . 'array-shapes', $options); + $links[] = $mainTypeLink . '{' . implode(', ', $itemsLinks) . '}'; + continue; } - } elseif ($type instanceof BaseDoc) { - $linkText = $type->name; - if ($title !== null) { - $linkText = $title; - $title = null; + + if ($type instanceof ObjectShape) { + $itemsLinks = $this->createLinksByShapeItems($type->getItems(), $context, $title, $options, $currentTypeDoc); + $mainTypeLink = $this->generateLink('object', self::PHPSTAN_TYPE_BASE_URL . 'object-shapes', $options); + $links[] = $mainTypeLink . '{' . implode(', ', $itemsLinks) . '}'; + continue; } - $links[] = $this->generateLink($linkText, $this->generateApiUrl($type->name), $options) . $postfix; - } - } - return implode('|', $links); - } + if ($type instanceof This && $currentTypeDoc !== null) { + $links[] = $this->createTypeLink($currentTypeDoc, null, '$this', $options); + continue; + } - /** - * @param MethodDoc $method - * @param TypeDoc $type - * @return string - */ - public function createMethodReturnTypeLink($method, $type) - { - if (!($type instanceof ClassDoc) || $type->isAbstract) { - return $this->createTypeLink($method->returnTypes, $type); - } + if ($type instanceof Static_ && !$type->getGenericTypes() && $currentTypeDoc !== null) { + $links[] = $this->createTypeLink($currentTypeDoc, null, null, $options); + continue; + } + + if (($link = $this->createLinkByTypeWithGenerics($type, $context, $title, $options, $currentTypeDoc)) !== null) { + $links[] = $link; + continue; + } + + if ($type instanceof Object_ && $type->getFqsen() !== null) { + /** @var class-string */ + $fqsen = (string) $type->getFqsen(); - $returnTypes = []; - foreach ($method->returnTypes as $returnType) { - if ($returnType !== 'static' && $returnType !== 'static[]') { - $returnTypes[] = $returnType; + if (($typeDoc = $this->getTypeDocByQualifiedClassName($fqsen, $context)) !== null) { + $links[] = $this->createTypeLink($typeDoc, $context, $typeDoc->name, $options); + continue; + } + + if (($templateType = $this->getTemplateType($fqsen, $context)) !== null) { + $links[] = $this->createTypeLink($templateType, $context, $title, $options, $currentTypeDoc); + continue; + } + + if (($phpStanType = $this->getPhpStanType($fqsen, $context)) !== null) { + $links[] = $this->createSubjectLink($phpStanType); + continue; + } + + if (($psalmType = $this->getPsalmType($fqsen, $context)) !== null) { + $links[] = $this->createSubjectLink($psalmType); + continue; + } - continue; + if (($phpStanTypeImport = $this->getPhpStanTypeImport($fqsen, $context)) !== null) { + $links[] = $this->createSubjectLink($phpStanTypeImport); + continue; + } + + if (($psalmTypeImport = $this->getPsalmTypeImport($fqsen, $context)) !== null) { + $links[] = $this->createSubjectLink($psalmTypeImport); + continue; + } + } } - $context = $this->apiContext; - if (isset($context->interfaces[$method->definedBy]) || isset($context->traits[$method->definedBy])) { - $replacement = $type->name; - } else { - $replacement = $method->definedBy; + if (is_object($type) && method_exists($type, '__toString')) { + $type = (string) $type; } - $returnTypes[] = str_replace('static', $replacement, $returnType); + $link = $this->createTypeLinkByType($type, $title, $options); + if ($link !== null) { + $links[] = $link; + } } - return $this->createTypeLink($returnTypes, $type); + return implode('|', array_unique($links)); } /** * creates a link to a subject - * @param BaseDoc $subject + * @param BaseDoc|PseudoTypeDoc|PseudoTypeImportDoc $subject * @param string|null $title * @param array $options additional HTML attributes for the link. * @param TypeDoc|null $type @@ -213,6 +355,17 @@ public function createMethodReturnTypeLink($method, $type) */ public function createSubjectLink($subject, $title = null, $options = [], $type = null) { + if ($subject instanceof PseudoTypeDoc) { + $href = $this->generateApiUrl($subject->parent->name) . "#{$subject->type}-type-{$subject->name}"; + return $this->generateLink($subject->name, $href, $options); + } + + if ($subject instanceof PseudoTypeImportDoc) { + $typeParentFqsen = (string) $subject->typeParentFqsen; + $href = $this->generateApiUrl(ltrim($typeParentFqsen, '\\')) . "#{$subject->type}-type-{$subject->typeName}"; + return $this->generateLink($subject->typeName, $href, $options); + } + if ($title === null) { if ($subject instanceof MethodDoc) { $title = $subject->name . '()'; @@ -231,7 +384,7 @@ public function createSubjectLink($subject, $title = null, $options = [], $type $link = $this->generateApiUrl($type->name) . '#' . $subject->name; if ($subject instanceof MethodDoc) { - $link .= '()'; + $link .= '()'; } $link .= '-detail'; @@ -252,8 +405,8 @@ private function resolveNamespace($context) if ($context instanceof TypeDoc) { return $context->namespace; } - if ($context->hasProperty('definedBy')) { - $type = $this->apiContext->getType($context); + if ($context->hasProperty('definedBy') && method_exists($context, '__toString')) { + $type = $this->apiContext->getType((string) $context); if ($type !== null) { return $type->namespace; } @@ -286,7 +439,7 @@ abstract public function generateApiUrl($typeName); public function generateGuideUrl($file) { //skip parsing external url - if ((strpos($file, 'https://') !== false) || (strpos($file, 'http://') !== false) ) { + if ((strpos($file, 'https://') !== false) || (strpos($file, 'http://') !== false)) { return $file; } @@ -298,4 +451,338 @@ public function generateGuideUrl($file) return rtrim($this->guideUrl, '/') . '/' . $this->guidePrefix . basename($file, '.md') . '.html' . $hash; } + + /** + * @param BaseDoc|string $type + * @param array{forcePhpStanLink?: bool, ...} $options + */ + private function createTypeLinkByType($type, ?string $title = null, array $options = []): ?string + { + if (isset($options['forcePhpStanLink'])) { + $isForcePhpStanLink = $options['forcePhpStanLink']; + unset($options['forcePhpStanLink']); + } else { + $isForcePhpStanLink = false; + } + + if (is_string($type)) { + $linkText = ltrim($type, '\\'); + if ($title !== null) { + $linkText = $title; + $title = null; + } + + // check if it is PHP internal class + if ( + (class_exists($type, false) || interface_exists($type, false) || trait_exists($type, false)) && + ($reflection = new \ReflectionClass($type)) && $reflection->isInternal() + ) { + return $this->generateLink( + $linkText, + self::PHP_CLASS_BASE_URL . strtolower(ltrim($type, '\\')), + $options + ); + } + + if ($isForcePhpStanLink) { + $link = $this->createPhpStanTypeLink($type, $options); + $link ??= $this->createPhpTypeLink($type, $linkText, $options); + } else { + $link = $this->createPhpTypeLink($type, $linkText, $options); + $link ??= $this->createPhpStanTypeLink($type, $options); + } + + return $link ?? $type; + } + + $linkText = $type->name; + if ($title !== null) { + $linkText = $title; + $title = null; + } + + return $this->generateLink($linkText, $this->generateApiUrl($type->name), $options); + } + + /** + * @param class-string $fqsen + */ + private function getPhpStanType(string $fqsen, ?BaseDoc $context): ?PseudoTypeDoc + { + if ($context === null) { + return null; + } + + $phpStanType = $context->phpStanTypes[$fqsen] ?? null; + if ($phpStanType === null) { + return $context->parent !== null ? $this->getPhpStanType($fqsen, $context->parent) : null; + } + + return $phpStanType; + } + + /** + * @param class-string $fqsen + */ + private function getPhpStanTypeImport(string $fqsen, ?BaseDoc $context): ?PseudoTypeImportDoc + { + if ($context === null) { + return null; + } + + $phpStanTypeImport = $context->phpStanTypeImports[$fqsen] ?? null; + if ($phpStanTypeImport === null) { + return $context->parent !== null ? $this->getPhpStanTypeImport($fqsen, $context->parent) : null; + } + + return $phpStanTypeImport; + } + + /** + * @param class-string $fqsen + */ + private function getPsalmType(string $fqsen, ?BaseDoc $context): ?PseudoTypeDoc + { + if ($context === null) { + return null; + } + + $psalmType = $context->psalmTypes[$fqsen] ?? null; + if ($psalmType === null) { + return $context->parent !== null ? $this->getPsalmType($fqsen, $context->parent) : null; + } + + return $psalmType; + } + + /** + * @param class-string $fqsen + */ + private function getPsalmTypeImport(string $fqsen, ?BaseDoc $context): ?PseudoTypeImportDoc + { + if ($context === null) { + return null; + } + + $psalmTypeImport = $context->psalmTypeImports[$fqsen] ?? null; + if ($psalmTypeImport === null) { + return $context->parent !== null ? $this->getPsalmTypeImport($fqsen, $context->parent) : null; + } + + return $psalmTypeImport; + } + + /** + * @param class-string $fqsen + */ + private function getTemplateType(string $fqsen, ?BaseDoc $context): ?Type + { + if ($context === null) { + return null; + } + + $template = $context->templates[$fqsen] ?? null; + if ($template === null) { + return $context->parent !== null ? $this->getTemplateType($fqsen, $context->parent) : null; + } + + return $template->getBound(); + } + + /** + * @param string $className + * @return ClassDoc|InterfaceDoc|TraitDoc|null + */ + private function getTypeDocByQualifiedClassName(string $className, ?BaseDoc $context): ?TypeDoc + { + $typeDoc = $this->apiContext->getType(ltrim($className, '\\')); + if ($typeDoc !== null) { + return $typeDoc; + } + + return $this->apiContext->getType($this->resolveNamespace($context) . '\\' . ltrim($className, '\\')); + } + + /** + * @param ShapeItem[] $items + * @return string[] + */ + private function createLinksByShapeItems( + array $items, + ?BaseDoc $context, + ?string $title, + array $options, + ?TypeDoc $currentTypeDoc + ): array { + $links = []; + + foreach ($items as $item) { + $itemKey = $item->getKey(); + if ($itemKey !== null && $itemKey !== '') { + $links[] = sprintf( + '%s%s: %s', + $itemKey, + $item->isOptional() ? '?' : '', + $this->createTypeLink($item->getValue(), $context, $title, $options, $currentTypeDoc) + ); + } else { + $links[] = $this->createTypeLink($item->getValue(), $context, $title, $options, $currentTypeDoc); + } + } + + return $links; + } + + /** + * @return string|null Link if the type has generics and null otherwise. + */ + private function createLinkByTypeWithGenerics( + Type $type, + ?BaseDoc $context, + ?string $title, + array $options, + ?TypeDoc $currentTypeDoc + ): ?string { + /** + * @param Type[] $genericTypes + */ + $generateLink = function (Type $mainType, array $genericTypes) use ( + $context, + $title, + $options, + $currentTypeDoc + ): string { + $genericTypesLinks = $this->createTypeLinksByTypes($genericTypes, $context, $title, $options); + $mainTypeLinkOptions = array_merge($options, ['forcePhpStanLink' => true]); + $mainTypeLink = $this->createTypeLink($mainType, $context, $title, $mainTypeLinkOptions, $currentTypeDoc); + return "{$mainTypeLink}<" . implode(', ', $genericTypesLinks) . '>'; + }; + + if ($type instanceof List_ && substr((string) $type, -1, 1) === '>') { + return $generateLink(new List_(), [$type->getValueType()]); + } + + if ($type instanceof NonEmptyList && substr((string) $type, -1, 1) === '>') { + return $generateLink(new NonEmptyList(), [$type->getValueType()]); + } + + if ($type instanceof Array_ && substr((string) $type, -1, 1) === '>') { + $genericTypes = $this->getGenericTypesByListType($type); + return $generateLink(new Array_(), $genericTypes); + } + + if ($type instanceof ClassString && $type->getFqsen() !== null) { + return $generateLink(new ClassString(), [new Object_($type->getFqsen())]); + } + + if ($type instanceof InterfaceString && $type->getFqsen() !== null) { + return $generateLink(new InterfaceString(), [new Object_($type->getFqsen())]); + } + + if ($type instanceof Static_ && $type->getGenericTypes()) { + return $generateLink(new Static_(), $type->getGenericTypes()); + } + + if ($type instanceof Self_ && $type->getGenericTypes()) { + return $generateLink(new Self_(), $type->getGenericTypes()); + } + + if ($type instanceof IntegerRange) { + $mainTypeLink = $this->createPhpStanTypeLink('int', $options); + return $mainTypeLink . '<' . $type->getMinValue() . ', ' . $type->getMaxValue() . '>'; + } + + if ($type instanceof Iterable_ && substr((string) $type, -1, 1) === '>') { + $genericTypes = $this->getGenericTypesByListType($type); + return $generateLink(new Iterable_(), $genericTypes); + } + + if ($type instanceof KeyOf) { + $genericTypeLink = $this->createTypeLink($type->getType(), $context, $title, $options); + return $this->createPhpStanTypeLink('key-of', $options) . "<{$genericTypeLink}>"; + } + + if ($type instanceof ValueOf) { + $genericTypeLink = $this->createTypeLink($type->getType(), $context, $title, $options); + return $this->createPhpStanTypeLink('value-of', $options) . "<{$genericTypeLink}>"; + } + + if ($type instanceof IntMask) { + $genericTypesLinks = $this->createTypeLinksByTypes($type->getTypes(), $context, $title, $options); + return $this->createPhpStanTypeLink('int-mask', $options) . '<' . implode(', ', $genericTypesLinks) . '>'; + } + + if ($type instanceof IntMaskOf) { + $genericTypeLink = $this->createTypeLink($type->getType(), $context, $title, $options); + return $this->createPhpStanTypeLink('int-mask-of', $options) . "<{$genericTypeLink}>"; + } + + if ($type instanceof Collection) { + $genericTypes = $this->getGenericTypesByListType($type); + return $generateLink(new Object_($type->getFqsen()), $genericTypes); + } + + return null; + } + + /** + * @param Type[] $types + */ + private function createTypeLinksByTypes( + array $types, + ?BaseDoc $context, + ?string $title, + array $options + ): array { + return array_map( + fn(Type $type) => $this->createTypeLink($type, $context, $title, $options), + $types + ); + } + + private function createPhpTypeLink(string $type, string $linkText, array $options): ?string + { + if (!in_array($type, self::PHP_TYPES)) { + return null; + } + + if (isset(self::PHP_TYPE_DISPLAY_ALIASES[$type])) { + $linkText = self::PHP_TYPE_DISPLAY_ALIASES[$type]; + } + + if (isset(self::PHP_TYPE_ALIASES[$type])) { + $type = self::PHP_TYPE_ALIASES[$type]; + } + + return $this->generateLink( + $linkText, + self::PHP_TYPE_BASE_URL . strtolower(ltrim($type, '\\')), + $options + ); + } + + private function createPhpStanTypeLink(string $type, array $options): ?string + { + foreach (self::PHPSTAN_TYPES_DOC_LINKS as $phpstanDocLink => $phpstanTypes) { + if (in_array($type, $phpstanTypes)) { + return $this->generateLink( + $type, + self::PHPSTAN_TYPE_BASE_URL . $phpstanDocLink, + $options + ); + } + } + + return null; + } + + /** + * @return array{0: Type, 1?: Type} + */ + private function getGenericTypesByListType(AbstractList $type): array + { + return $type->getOriginalKeyType() !== null + ? [$type->getOriginalKeyType(), $type->getValueType()] + : [$type->getValueType()]; + } } diff --git a/templates/html/ApiRenderer.php b/templates/html/ApiRenderer.php index facee21..529064d 100644 --- a/templates/html/ApiRenderer.php +++ b/templates/html/ApiRenderer.php @@ -20,6 +20,7 @@ use yii\web\AssetManager; use yii\web\View; use Yii; +use yii\apidoc\models\TypeDoc; /** * The base class for HTML API documentation renderers. @@ -259,20 +260,16 @@ public function renderPropertySignature($property, $context = null) } return '' . implode(' ', $definition) . ' ' - . '' . $this->createTypeLink($property->types, $context) . '' + . '' . $this->createTypeLink($property->type, $property) . '' . ' ' . $this->createSubjectLink($property, $property->name) . ' ' . ApiMarkdown::highlight('= ' . $this->renderDefaultValue($property->defaultValue), 'php'); } - /** - * @param MethodDoc $method - * @return string - */ - public function renderMethodSignature($method, $context = null) + public function renderMethodSignature(MethodDoc $method, ?TypeDoc $context = null): string { $params = []; foreach ($method->params as $param) { - $params[] = '' . $this->createTypeLink($param->types, $context) . ' ' + $params[] = '' . $this->createTypeLink($param->type, $method) . ' ' . ($param->isPassedByReference ? '&' : '') . ApiMarkdown::highlight( $param->name @@ -292,7 +289,7 @@ public function renderMethodSignature($method, $context = null) return '' . implode(' ', $definition) . ' ' . '' . ($method->isReturnByReference ? '&' : '') - . $this->createTypeLink($method->returnTypes, $context) . ' ' + . $this->createTypeLink($method->returnType, $method, null, [], $context) . ' ' . '' . $this->createSubjectLink($method, $method->name) . '' . str_replace(' ', ' ', ' ( ' . implode(', ', $params) . ' )'); } diff --git a/templates/html/views/constSummary.php b/templates/html/views/constSummary.php index 1515321..9c9989b 100644 --- a/templates/html/views/constSummary.php +++ b/templates/html/views/constSummary.php @@ -64,7 +64,7 @@ - createTypeLink($constant->definedBy) ?> + createTypeLink($constant->definedBy, $type) ?> diff --git a/templates/html/views/eventDetails.php b/templates/html/views/eventDetails.php index a0456db..979aa95 100644 --- a/templates/html/views/eventDetails.php +++ b/templates/html/views/eventDetails.php @@ -48,7 +48,7 @@ event of type - createTypeLink($event->types) ?> + createTypeLink($event->type, $type) ?> since) ? "(available since version $event->since)" : '' ?> diff --git a/templates/html/views/eventSummary.php b/templates/html/views/eventSummary.php index ec07ee6..3110e38 100644 --- a/templates/html/views/eventSummary.php +++ b/templates/html/views/eventSummary.php @@ -42,14 +42,14 @@ createSubjectLink($event, null, [], $type) ?> - createTypeLink($event->types) ?> + createTypeLink($event->type, $type) ?> shortDescription, $event->definedBy, true) ?> since)) { ?> (available since version since ?>) - createTypeLink($event->definedBy) ?> + createTypeLink($event->definedBy, $type) ?> diff --git a/templates/html/views/methodDetails.php b/templates/html/views/methodDetails.php index ad602c0..839356e 100644 --- a/templates/html/views/methodDetails.php +++ b/templates/html/views/methodDetails.php @@ -1,17 +1,18 @@ context; $methods = $type->methods; @@ -94,7 +95,7 @@ class="definedBy !== $type->name ? 'inherited': '' ?>"> params as $param) { ?> name, 'php') ?> - createTypeLink($param->types) ?> + createTypeLink($param->type, $method) ?> description, $method->definedBy) ?> @@ -104,19 +105,19 @@ class="definedBy !== $type->name ? 'inherited': '' ?>"> return)) { ?> return - createMethodReturnTypeLink($method, $type) ?> + createTypeLink($method->returnType, $method, null, [], $type) ?> return, $method->definedBy) ?> - exceptions as $exception => $description) { ?> + exceptions as $exception) { ?> throws - createTypeLink($exception) ?> + createTypeLink($exception->getType(), $method) ?> - definedBy) ?> + getDescription(), $method->definedBy) ?> diff --git a/templates/html/views/propertySummary.php b/templates/html/views/propertySummary.php index 8c70adc..2168d69 100644 --- a/templates/html/views/propertySummary.php +++ b/templates/html/views/propertySummary.php @@ -51,9 +51,9 @@ ) { ?> createSubjectLink($property, null, [], $type) ?> - createTypeLink($property->types) ?> + createTypeLink($property->type, $property) ?> shortDescription, $property->definedBy, true) ?> - createTypeLink($property->definedBy) ?> + createTypeLink($property->definedBy, $type) ?> diff --git a/templates/html/views/pseudoTypes.php b/templates/html/views/pseudoTypes.php new file mode 100644 index 0000000..af7ca21 --- /dev/null +++ b/templates/html/views/pseudoTypes.php @@ -0,0 +1,38 @@ +context; + +?> + +
+

+ + + + + + + + + + + + + + + + + +
NameValue
createSubjectLink($type) ?>createTypeLink($type->value) ?>
+
diff --git a/templates/html/views/type.php b/templates/html/views/type.php index a900394..9094ba5 100644 --- a/templates/html/views/type.php +++ b/templates/html/views/type.php @@ -1,17 +1,20 @@ context; ?>

render('@yii/apidoc/templates/html/views/changelog', ['doc' => $type]) ?> +phpStanTypes): ?> + render('@yii/apidoc/templates/html/views/pseudoTypes', [ + 'types' => $type->phpStanTypes, + 'title' => 'PHPStan Types', + ]) ?> + + +psalmTypes): ?> + render('@yii/apidoc/templates/html/views/pseudoTypes', [ + 'types' => $type->psalmTypes, + 'title' => 'Psalm Types', + ]) ?> + + render('@yii/apidoc/templates/html/views/propertySummary', ['type' => $type, 'protected' => false]) ?> render('@yii/apidoc/templates/html/views/propertySummary', ['type' => $type, 'protected' => true]) ?> diff --git a/templates/json/ApiRenderer.php b/templates/json/ApiRenderer.php index cc4ea52..54f5def 100644 --- a/templates/json/ApiRenderer.php +++ b/templates/json/ApiRenderer.php @@ -1,4 +1,5 @@ classes, $context->interfaces, $context->traits); - foreach($types as $name => $type) { - $types[$name] = (array) $type; + foreach ($types as $name => $type) { + $types[$name] = (array) $this->removeParentFieldsRecursive($type); if ($type instanceof ClassDoc) { $types[$name]['type'] = 'class'; } elseif ($type instanceof InterfaceDoc) { @@ -42,7 +42,37 @@ public function render($context, $targetDir) $types[$name]['type'] = 'trait'; } } - file_put_contents($targetDir . '/types.json', json_encode($types, JSON_PRETTY_PRINT)); + file_put_contents($targetDir . '/types.json', json_encode($types, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); + } + + /** + * Removes the `parent` fields recursively so that the data could be converted to JSON format.. + * @param array|object $data + * @return array|object + */ + private function removeParentFieldsRecursive($data) + { + if (is_array($data)) { + foreach ($data as $key => $value) { + if ($key === 'parent') { + unset($data[$key]); + } elseif (is_object($value) || is_array($value)) { + $data[$key] = $this->removeParentFieldsRecursive($value); + } + } + + return $data; + } + + foreach (get_object_vars($data) as $key => $value) { + if ($key === 'parent') { + unset($data->$key); + } elseif (is_object($value) || is_array($value)) { + $data->$key = $this->removeParentFieldsRecursive($value); + } + } + + return $data; } /** diff --git a/tests/commands/ApiControllerTest.php b/tests/commands/ApiControllerTest.php index c15b776..cdf9724 100644 --- a/tests/commands/ApiControllerTest.php +++ b/tests/commands/ApiControllerTest.php @@ -56,7 +56,7 @@ protected function generateApi($sourceDirs, $targetDir = '@runtime', array $args // Tests : - public function testNoFiles() + public function testNoFiles(): void { $output = $this->generateApi(Yii::getAlias('@yiiunit/apidoc/data/guide')); @@ -64,7 +64,7 @@ public function testNoFiles() $this->assertStringContainsString('Error: No files found to process', $output); } - public function testGenerateBootstrap() + public function testGenerateBootstrap(): void { $sourceFilesDir = Yii::getAlias('@yiiunit/apidoc/data/api'); $output = $this->generateApi($sourceFilesDir, '@runtime', ['template' => 'bootstrap']); @@ -91,6 +91,7 @@ public function testGenerateBootstrap() $fileContent = str_replace('> $', '>$', $fileContent); $fileContent = str_replace('> <', '><', $fileContent); $fileContent = str_replace('= ', '= ', $fileContent); + $fileContent = str_replace(' <', ' <', $fileContent); $this->assertMatchesHtmlSnapshot($fileContent); $filesCount++; @@ -110,4 +111,16 @@ public function testGenerateBootstrap() $errorsContent = preg_replace('/(\s*\[file\] => ).*(\/tests\/.*\.php)/', '$1$2', $errorsContent); $this->assertMatchesTextSnapshot($errorsContent); } + + public function testGenerateJson(): void + { + $sourceFilesDir = Yii::getAlias('@yiiunit/apidoc/data/api'); + $output = $this->generateApi($sourceFilesDir, '@runtime', ['template' => 'json']); + + $this->assertNotEmpty($output); + $this->assertStringContainsString('Updating cross references and backlinks... done.', $output); + + $content = file_get_contents(Yii::getAlias('@runtime') . '/types.json'); + $this->assertNotEmpty($content); + } } diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__1.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__1.html index b665dd1..326c24c 100644 --- a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__1.html +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__1.html @@ -44,9 +44,25 @@ Dog yiiunit\apidoc\data\api\base +yiiunit\apidoc\data\api\db +yiiunit\apidoc\data\api\di +yiiunit\apidoc\data\api\web

Abstract Class yiiunit\apidoc\data\api\animal\Animal

@@ -87,6 +103,29 @@

Abstract Class yiiunit\apidoc\data\api\animal\Animal

+ +
+

PHPStan Types

+ + + + + + + + + + + + + + + +
NameValue
AnimalData +array{name: string, birthDate: integer}
+
+ +
@@ -113,12 +152,122 @@

Public Properties

integer animal age in seconds. yiiunit\apidoc\data\api\animal\Animal + + + $arrayShapeProperty + +array{someKey: string} + + yiiunit\apidoc\data\api\animal\Animal + + + $arrayWithParenthesesProperty + (yiiunit\apidoc\data\api\animal\Cat|yiiunit\apidoc\data\api\animal\Dog)[] + + yiiunit\apidoc\data\api\animal\Animal + + + $arrayWithoutParenthesesProperty + +yiiunit\apidoc\data\api\animal\Dog[]|yiiunit\apidoc\data\api\animal\Cat[] + + yiiunit\apidoc\data\api\animal\Animal $birthDate integer Animal birth date as a UNIX timestamp. yiiunit\apidoc\data\api\animal\Animal + + + $callableProperty + callable + + yiiunit\apidoc\data\api\animal\Animal + + + $closureProperty + callable + + yiiunit\apidoc\data\api\animal\Animal + + + $genericArrayWithKeyProperty + +array<array-key, string[]> + + yiiunit\apidoc\data\api\animal\Animal + + + $genericArrayWithoutKeyProperty + +string[] + + yiiunit\apidoc\data\api\animal\Animal + + + $intMaskOfProperty + +int-mask-of<1|2|4> + + yiiunit\apidoc\data\api\animal\Animal + + + $intMaskProperty + +int-mask<1, 2, 4> + + yiiunit\apidoc\data\api\animal\Animal + + + $intMaskReadProperty + +int-mask<1, 2, 4> + Some description (intMaskReadProperty). + yiiunit\apidoc\data\api\animal\Animal + + + $intRangeProperty + +int<0, max> + + yiiunit\apidoc\data\api\animal\Animal + + + $intersectionType + +yiiunit\apidoc\data\api\animal\Cat&yiiunit\apidoc\data\api\animal\Dog + + + yiiunit\apidoc\data\api\animal\Animal + + + $iterableProperty + +iterable<integer, string> + + yiiunit\apidoc\data\api\animal\Animal + + + $iterableWithoutKeyProperty + +iterable<string> + + yiiunit\apidoc\data\api\animal\Animal + + + $keyOfProperty + +key-of<self::COLORS> + + yiiunit\apidoc\data\api\animal\Animal + + + $keyOfWriteProperty + +key-of<self::COLORS> + Some description (keyOfWriteProperty). + yiiunit\apidoc\data\api\animal\Animal $name @@ -127,14 +276,23 @@

Public Properties

yiiunit\apidoc\data\api\animal\Animal - $propertyWithoutDoc - string + $objectShapeProperty + +object{someKey: string} yiiunit\apidoc\data\api\animal\Animal - $propertyWithoutDocAndTypeHint - + $valueOfAnnotationProperty + +value-of<self::COLORS> + Some description (valueOfAnnotationProperty). + yiiunit\apidoc\data\api\animal\Animal + + + $valueOfProperty + +value-of<self::COLORS> yiiunit\apidoc\data\api\animal\Animal @@ -161,14 +319,44 @@

Public Methods

+ asArray() + + yiiunit\apidoc\data\api\animal\Animal + + + asStdClass() + + yiiunit\apidoc\data\api\animal\Animal + + getAge() Returns animal age in seconds. yiiunit\apidoc\data\api\animal\Animal + + + getIntMaskProperty() + + yiiunit\apidoc\data\api\animal\Animal + + + getMixed() + + yiiunit\apidoc\data\api\animal\Animal + + + getSelfWithGenerics() + + yiiunit\apidoc\data\api\animal\Animal getSomething() yiiunit\apidoc\data\api\animal\Animal + + + getStatic() + + yiiunit\apidoc\data\api\animal\Animal isOlder() @@ -184,6 +372,21 @@

Public Methods

setBirthDate() yiiunit\apidoc\data\api\animal\Animal + + + setIntMaskOfProperty() + + yiiunit\apidoc\data\api\animal\Animal + + + setKeyOfProperty() + + yiiunit\apidoc\data\api\animal\Animal + + + setName() + + yiiunit\apidoc\data\api\animal\Animal
@@ -273,9 +476,9 @@

Property Details

- + - $birthDate + $arrayShapeProperty public property @@ -284,11 +487,10 @@

Property Details

-

Animal birth date as a UNIX timestamp.

-
+
-publicinteger$birthDate= null +publicarray{someKey: string}$arrayShapeProperty= null
@@ -296,9 +498,9 @@

Property Details

- + - $name + $arrayWithParenthesesProperty public property @@ -307,11 +509,10 @@

Property Details

-

Animal name.

-
+
@@ -319,9 +520,9 @@

Property Details

- + - $propertyWithoutDoc + $arrayWithoutParenthesesProperty public property @@ -333,7 +534,7 @@

Property Details

@@ -341,9 +542,9 @@

Property Details

- + - $propertyWithoutDocAndTypeHint + $birthDate public property @@ -352,162 +553,807 @@

Property Details

-
+

Animal birth date as a UNIX timestamp.

+
-
- -

Method Details

- -
-

Hide inherited methods

- -
+
- + - getAge() - + $callableProperty - public method + public property
-

Returns animal age in seconds.

- - - - - - - - - - -
-publicintegergetAge ( )
returninteger -

Animal age in seconds.

-
- - + - -

- -

-
-
-
-                publicfunctiongetAge()
-{
-    return time() - $this->birthDate;
-}
-
-            
-
-
-
+
- + - getSomething() - + $closureProperty - public method + public property
-

- - - - - - - - - - - - - - - - - - - - - - - - - -
-publicintegergetSomething ( mixed$test, integer$test2, integer|string$test3 )
$testmixed -
$test2integer -
$test3 -integer|string - -
returninteger -
- - +
+publiccallable$closureProperty= null +
-
+
- + - isOlder() - + $genericArrayWithKeyProperty - public method + public property
-

Checks whether the animal is older than the specified time.

- - - - - - - - - - + + + + +
+
+ + + + $genericArrayWithoutKeyProperty + + public property + +
+ + +
+ +
+ + + + +
+
+
+ + + + $intMaskOfProperty + + public property + +
+ + +
+ +
+ +
+publicint-mask-of<1|2|4>$intMaskOfProperty= null +
+ + +
+
+
+ + + + $intMaskProperty + + public property + +
+ + +
+ +
+ +
+publicint-mask<1, 2, 4>$intMaskProperty= null +
+ + +
+
+
+ + + + $intMaskReadProperty + + public property + +
+ + +
+ +

Some description (intMaskReadProperty).

+
+ +
+publicint-mask<1, 2, 4>$intMaskReadProperty= null +
+ + +
+
+
+ + + + $intRangeProperty + + public property + +
+ + +
+ +
+ +
+publicint<0, max>$intRangeProperty= null +
+ + +
+
+
+ + + + $intersectionType + + public property + +
+ + +
+ +
+ + + + +
+
+
+ + + + $iterableProperty + + public property + +
+ + +
+ +
+ + + + +
+
+
+ + + + $iterableWithoutKeyProperty + + public property + +
+ + +
+ +
+ + + + +
+
+
+ + + + $keyOfProperty + + public property + +
+ + +
+ +
+ +
+publickey-of<self::COLORS>$keyOfProperty= null +
+ + +
+
+
+ + + + $keyOfWriteProperty + + public property + +
+ + +
+ +

Some description (keyOfWriteProperty).

+
+ +
+publickey-of<self::COLORS>$keyOfWriteProperty= null +
+ + +
+
+
+ + + + $name + + public property + +
+ + +
+ +

Animal name.

+
+ +
+publicstring$name= null +
+ + +
+
+
+ + + + $objectShapeProperty + + public property + +
+ + +
+ +
+ +
+publicobject{someKey: string}$objectShapeProperty= null +
+ + +
+
+
+ + + + $valueOfAnnotationProperty + + public property + +
+ + +
+ +

Some description (valueOfAnnotationProperty).

+
+ +
+publicvalue-of<self::COLORS>$valueOfAnnotationProperty= null +
+ + +
+
+
+ + + + $valueOfProperty + + public property + +
+ + +
+ +
+ +
+publicvalue-of<self::COLORS>$valueOfProperty= null +
+ + +
+ + +

Method Details

+ +
+

Hide inherited methods

+ +
+
+ + + + asArray() + + + public method + +
+ + +
+ +

+
+ +
-publicbooleanisOlder ( integer$date )
$dateinteger -

Date as a UNIX timestamp.

-
+ + +
+publicAnimalDataasArray ( )
+ + + + +

+ +

+
+
+
+                publicfunctionasArray()
+{
+    return [
+        'name' => $this->name,
+        'birthDate' => $this->birthDate,
+    ];
+}
+
+            
+
+
+ +
+
+
+ + + + asStdClass() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicobject{name: string, birthDate: integer}asStdClass ( )
+ + + + +

+ +

+
+
+
+                publicfunctionasStdClass()
+{
+    return (object) $this->asArray();
+}
+
+            
+
+
+ +
+
+
+ + + + getAge() + + + public method + +
+ + +
+ +

Returns animal age in seconds.

+
+ + + + + + + + + + + +
+publicintegergetAge ( )
returninteger +

Animal age in seconds.

+
+ + + + +

+ +

+
+
+
+                publicfunctiongetAge()
+{
+    return time() - $this->birthDate;
+}
+
+            
+
+
+ +
+
+
+ + + + getIntMaskProperty() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicint-mask<1, 2, 4>getIntMaskProperty ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetIntMaskProperty(): int
+{
+    return$this->intMaskProperty;
+}
+
+            
+
+
+ +
+
+
+ + + + getMixed() + + + public method + +
+ + +
+ +

+
+ + + + + + + + + + + +
+publicmixedgetMixed ( )
returnmixed +
+ + + + +
+
+
+ + + + getSelfWithGenerics() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicself<yiiunit\apidoc\data\api\animal\Cat>getSelfWithGenerics ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetSelfWithGenerics()
+{
+    return$this;
+}
+
+            
+
+
+ +
+
+
+ + + + getSomething() + + + public method + +
+ + +
+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+publicintegergetSomething ( mixed$test, integer$test2, integer|string$test3 )
$testmixed +
$test2integer +
$test3 +integer|string + +
returninteger +
+ + + + +
+
+
+ + + + getStatic() + + + public static method + +
+ + +
+ +

+
+ + + + +
+public staticyiiunit\apidoc\data\api\animal\AnimalgetStatic ( )
+ + + + +

+ +

+
+
+
+                publicstaticfunctiongetStatic()
+{
+    return Yii::createObject([
+        'class' => get_called_class(),
+    ]);
+}
+
+            
+
+
+ +
+
+
+ + + + isOlder() + + + public method + +
+ + +
+ +

Checks whether the animal is older than the specified time.

+
+ + + + + + + + + + +
+publicbooleanisOlder ( integer$date )
$dateinteger +

Date as a UNIX timestamp.

+
@@ -630,6 +1476,170 @@

Method Details

$this->birthDate = $birthDate; return$this; } + + +
+
+ +
+
+
+ + + + setIntMaskOfProperty() + + + public method + +
+ + +
+ +

+
+ + + + + + + + + + + +
+public$thissetIntMaskOfProperty ( int-mask-of<1|2|4>$newValue )
$newValue +int-mask-of<1|2|4> +
+ + + + +

+ +

+
+
+
+                publicfunctionsetIntMaskOfProperty(int $newValue): self
+{
+    $this->intMaskOfProperty = $newValue;
+    return$this;
+}
+
+            
+
+
+ +
+
+
+ + + + setKeyOfProperty() + + + public method + +
+ + +
+ +

+
+ + + + + + + + + + + +
+public$thissetKeyOfProperty ( key-of<self::COLORS>$newValue )
$newValue +key-of<self::COLORS> +
+ + + + +

+ +

+
+
+
+                publicfunctionsetKeyOfProperty(int $newValue): self
+{
+    $this->keyOfProperty = $newValue;
+    return$this;
+}
+
+            
+
+
+ +
+
+
+ + + + setName() + + + public method + +
+ + +
+ +

+
+ + + + + + + + + + + +
+public$thissetName ( string$newName )
$newNamestring +
+ + + + +

+ +

+
+
+
+                publicfunctionsetName(string $newName): self
+{
+    $this->name = trim($newName);
+    return$this;
+}
 
             
diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__10.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__10.html new file mode 100644 index 0000000..35d1f6a --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__10.html @@ -0,0 +1,334 @@ + + + + + + + + + + + + + ActiveQueryInterface, yiiunit\apidoc\data\api\db\ActiveQueryInterface - Yii Framework 2.0 API Documentation + + + +
+ + + + +
+ +
+

Interface yiiunit\apidoc\data\api\db\ActiveQueryInterface

+ + + + + + + + + + + +
Implemented byyiiunit\apidoc\data\api\db\ActiveQuery
+ +
+

+ +
+ + + + + + + + + +
+

Public Methods

+ +

Hide inherited methods

+ + + + + + + + + + + + + + + + + + + + + + + +
MethodDescriptionDefined By
all()yiiunit\apidoc\data\api\db\ActiveQueryInterface
one()yiiunit\apidoc\data\api\db\ActiveQueryInterface
+
+ + + + + + +

Method Details

+ +
+

Hide inherited methods

+ +
+
+ + + + all() + + + public abstract method + +
+ + +
+ +

+
+ + + + +
+public abstractmixedall ( )
+ + + + +

+ +

+
+
+
+                publicfunctionall();
+
+            
+
+
+ +
+
+
+ + + + one() + + + public abstract method + +
+ + +
+ +

+
+ + + + +
+public abstractmixedone ( )
+ + + + +

+ +

+
+
+
+                publicfunctionone();
+
+            
+
+
+ +
+
+
+
+ + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__11.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__11.html new file mode 100644 index 0000000..cf73639 --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__11.html @@ -0,0 +1,212 @@ + + + + + + + + + + + + + ActiveRecord, yiiunit\apidoc\data\api\db\ActiveRecord - Yii Framework 2.0 API Documentation + + + +
+ + + + + + + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__12.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__12.html new file mode 100644 index 0000000..622b249 --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__12.html @@ -0,0 +1,208 @@ + + + + + + + + + + + + + ActiveRecordInterface, yiiunit\apidoc\data\api\db\ActiveRecordInterface - Yii Framework 2.0 API Documentation + + + +
+ + + + + + + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__13.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__13.html new file mode 100644 index 0000000..2b24f48 --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__13.html @@ -0,0 +1,420 @@ + + + + + + + + + + + + + ActiveRelationTrait, yiiunit\apidoc\data\api\db\ActiveRelationTrait - Yii Framework 2.0 API Documentation + + + +
+ + + + +
+ +
+

Trait yiiunit\apidoc\data\api\db\ActiveRelationTrait

+ + + + + + + +
+ +
+

ActiveRelationTrait implements the common methods and properties for active record relational queries.

+ +
+ + + + + + + + + + + + + + + + + + +

Property Details

+ +
+

Hide inherited properties

+ +
+
+ + + + $modelClass + + public property + +
+ + +
+ +
+ + + + +
+
+
+ + + + $someProperty + + public property + +
+ + +
+ +
+ +
+public(integer|string)[]$someProperty= null +
+ + +
+
+ +

Method Details

+ +
+

Hide inherited methods

+ +
+
+ + + + all() + + + public method + +
+ + + + + + + + + + + + + + + + + + + +
+publiclist<yiiunit\apidoc\data\api\db\ActiveRecordInterface>all ( mixed$db )
$dbmixed +
return +list<yiiunit\apidoc\data\api\db\ActiveRecordInterface> +
+ + + + +
+
+
+ + + + one() + + + public method + +
+ + + + + + + + + + + + + + + + + + + +
+publicyiiunit\apidoc\data\api\db\ActiveRecordInterface|array|nullone ( mixed$db )
$dbmixed +
return +yiiunit\apidoc\data\api\db\ActiveRecordInterface|array|null + +
+ + + + +
+
+
+
+ + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__14.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__14.html new file mode 100644 index 0000000..740f3ae --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__14.html @@ -0,0 +1,528 @@ + + + + + + + + + + + + + BatchQueryResult, yiiunit\apidoc\data\api\db\BatchQueryResult - Yii Framework 2.0 API Documentation + + + +
+ + + + +
+ +
+

Class yiiunit\apidoc\data\api\db\BatchQueryResult

+ + + + + + + + + + + + + + + +
Inheritance +yiiunit\apidoc\data\api\db\BatchQueryResult » +yii\base\Component
ImplementsIterator
+ +
+

BatchQueryResult represents a batch query from which you can retrieve data in batches.

+ +
+ + + + + + + + + +
+

Public Methods

+ +

Hide inherited methods

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodDescriptionDefined By
current()Returns the current dataset.yiiunit\apidoc\data\api\db\BatchQueryResult
key()Returns the index of the current dataset.yiiunit\apidoc\data\api\db\BatchQueryResult
next()Moves the internal pointer to the next dataset.yiiunit\apidoc\data\api\db\BatchQueryResult
rewind()Resets the iterator to the initial state.yiiunit\apidoc\data\api\db\BatchQueryResult
valid()Returns whether there is a valid dataset at the current position.yiiunit\apidoc\data\api\db\BatchQueryResult
+
+ + + + + + +

Method Details

+ +
+

Hide inherited methods

+ +
+
+ + + + current() + + + public method + +
+ + +
+ +

Returns the current dataset.

+

This method is required by the interface Iterator.

+
+ + + + + + + + + + + +
+publicmixedcurrent ( )
returnmixed +

The current dataset.

+
+ + + + +

+ +

+
+
+
+                #[\ReturnTypeWillChange]
+publicfunctioncurrent()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + key() + + + public method + +
+ + +
+ +

Returns the index of the current dataset.

+

This method is required by the interface Iterator.

+
+ + + + + + + + + + + +
+publicintegerkey ( )
returninteger +

The index of the current row.

+
+ + + + +

+ +

+
+
+
+                #[\ReturnTypeWillChange]
+publicfunctionkey()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + next() + + + public method + +
+ + +
+ +

Moves the internal pointer to the next dataset.

+

This method is required by the interface Iterator.

+
+ + + + +
+publicmixednext ( )
+ + + + +

+ +

+
+
+
+                #[\ReturnTypeWillChange]
+publicfunctionnext()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + rewind() + + + public method + +
+ + +
+ +

Resets the iterator to the initial state.

+

This method is required by the interface Iterator.

+
+ + + + +
+publicmixedrewind ( )
+ + + + +

+ +

+
+
+
+                #[\ReturnTypeWillChange]
+publicfunctionrewind()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + valid() + + + public method + +
+ + +
+ +

Returns whether there is a valid dataset at the current position.

+

This method is required by the interface Iterator.

+
+ + + + + + + + + + + +
+publicbooleanvalid ( )
returnboolean +

Whether there is a valid dataset at the current position.

+
+ + + + +

+ +

+
+
+
+                #[\ReturnTypeWillChange]
+publicfunctionvalid()
+{
+}
+
+            
+
+
+ +
+
+
+
+ + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__15.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__15.html new file mode 100644 index 0000000..41e7c2d --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__15.html @@ -0,0 +1,329 @@ + + + + + + + + + + + + + Container, yiiunit\apidoc\data\api\di\Container - Yii Framework 2.0 API Documentation + + + +
+ + + + +
+ +
+

Class yiiunit\apidoc\data\api\di\Container

+ + + + + + + + + + + +
Inheritanceyiiunit\apidoc\data\api\di\Container
+ +
+

Container implements a dependency injection container.

+ +
+ + + + + + + + + +
+

Public Methods

+ +

Hide inherited methods

+ + + + + + + + + + + + + + + + + + +
MethodDescriptionDefined By
get()Returns an instance of the requested class.yiiunit\apidoc\data\api\di\Container
+
+ + + + + + +

Method Details

+ +
+

Hide inherited methods

+ +
+
+ + + + get() + + + public method + +
+ + +
+ +

Returns an instance of the requested class.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+publicobjectget ( string|class-string<object>$class, array<array-key, mixed>$params = [], array<string, mixed>$config = [] )
$class +string|class-string<object> +

The class name, or an alias name (e.g. foo).

+
$params +array<array-key, mixed> +

A list of constructor parameter values. Use one of two definitions:

+
    +
  • Parameters as name-value pairs, for example: ['posts' => PostRepository::class].
  • +
  • Parameters in the order they appear in the constructor declaration. If you want to skip some parameters, +you should index the remaining ones with the integers that represent their positions in the constructor +parameter list. +Dependencies indexed by name and by position in the same array are not allowed.
  • +
+
$config +array<string, mixed> +

A list of name-value pairs that will be used to initialize the object properties.

+
returnobject +

An instance of the requested class.

+
+ + + + +

+ +

+
+
+
+                publicfunctionget($class, $params = [], $config = [])
+{
+}
+
+            
+
+
+ +
+
+
+
+ + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__16.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__16.html new file mode 100644 index 0000000..86adb93 --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__16.html @@ -0,0 +1,272 @@ + + + + + + + + + + + + + AssetBundle, yiiunit\apidoc\data\api\web\AssetBundle - Yii Framework 2.0 API Documentation + + + +
+ + + + +
+ +
+

Class yiiunit\apidoc\data\api\web\AssetBundle

+ + + + + + + + + + + +
Inheritanceyiiunit\apidoc\data\api\web\AssetBundle
+ +
+

AssetBundle represents a collection of asset files, such as CSS, JS, images.

+ +
+ + + + + + + +
+

Public Properties

+ +

Hide inherited properties

+ + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescriptionDefined By
$publishOptionsPublishOptionsThe options to be passed to yiiunit\apidoc\data\api\web\AssetManager::publish() when the asset bundle is being published.yiiunit\apidoc\data\api\web\AssetBundle
+
+ + + + + + + + +

Property Details

+ +
+

Hide inherited properties

+ +
+
+ + + + $publishOptions + + public property + +
+ + +
+ +

The options to be passed to yiiunit\apidoc\data\api\web\AssetManager::publish() when the asset bundle +is being published.

+
+ + + + +
+
+
+
+ + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__17.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__17.html new file mode 100644 index 0000000..4931133 --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__17.html @@ -0,0 +1,357 @@ + + + + + + + + + + + + + AssetManager, yiiunit\apidoc\data\api\web\AssetManager - Yii Framework 2.0 API Documentation + + + +
+ + + + +
+ +
+

Class yiiunit\apidoc\data\api\web\AssetManager

+ + + + + + + + + + + +
Inheritance +yiiunit\apidoc\data\api\web\AssetManager » +yiiunit\apidoc\data\api\base\Component +
+ +
+

AssetManager manages asset bundle configuration and loading.

+ +
+ + + + +
+

PHPStan Types

+ + + + + + + + + + + + + + + +
NameValue
PublishOptions +array{only?: string[], except?: string[], caseSensitive?: boolean, beforeCopy?: callable, afterCopy?: callable, forceCopy?: boolean}
+
+ + +
+

Psalm Types

+ + + + + + + + + + + + + + + +
NameValue
PublishOptions +array{only?: string[], except?: string[], caseSensitive?: boolean, beforeCopy?: callable, afterCopy?: callable, forceCopy?: boolean}
+
+ + + + + +
+

Public Methods

+ +

Hide inherited methods

+ + + + + + + + + + + + + + + + + + +
MethodDescriptionDefined By
publish()Publishes a file or a directory.yiiunit\apidoc\data\api\web\AssetManager
+
+ + + + + + +

Method Details

+ +
+

Hide inherited methods

+ +
+
+ + + + publish() + + + public method + +
+ + +
+ +

Publishes a file or a directory.

+
+ + + + + + + + + + + + + + + + + + + + + +
+publicnon-empty-arraypublish ( non-empty-string$path, PublishOptions$options = [] )
$pathnon-empty-string +

The asset (file or directory) to be published

+
$optionsPublishOptions +

The options to be applied when publishing a directory.

+
returnnon-empty-array +

The path (directory or file path) and the URL that the asset is published as.

+
+ + + + +

+ +

+
+
+
+                publicfunctionpublish($path, $options = [])
+{
+}
+
+            
+
+
+ +
+
+
+
+ + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__18.txt b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__18.txt new file mode 100644 index 0000000..da7445e --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__18.txt @@ -0,0 +1,381 @@ +Array +( + [0] => Array + ( + [line] => 56 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$arrayWithParenthesesProperty' + ) + + [1] => Array + ( + [line] => 60 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$arrayWithoutParenthesesProperty' + ) + + [2] => Array + ( + [line] => 64 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$intRangeProperty' + ) + + [3] => Array + ( + [line] => 68 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$arrayShapeProperty' + ) + + [4] => Array + ( + [line] => 72 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$objectShapeProperty' + ) + + [5] => Array + ( + [line] => 76 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$iterableProperty' + ) + + [6] => Array + ( + [line] => 80 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$iterableWithoutKeyProperty' + ) + + [7] => Array + ( + [line] => 84 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$genericArrayWithoutKeyProperty' + ) + + [8] => Array + ( + [line] => 88 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$genericArrayWithKeyProperty' + ) + + [9] => Array + ( + [line] => 92 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$callableProperty' + ) + + [10] => Array + ( + [line] => 96 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$closureProperty' + ) + + [11] => Array + ( + [line] => 100 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$intersectionType' + ) + + [12] => Array + ( + [line] => 104 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$intMaskProperty' + ) + + [13] => Array + ( + [line] => 108 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$intMaskOfProperty' + ) + + [14] => Array + ( + [line] => 112 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$valueOfProperty' + ) + + [15] => Array + ( + [line] => 116 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for element '$keyOfProperty' + ) + + [16] => Array + ( + [line] => 146 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for Method 'asArray' + ) + + [17] => Array + ( + [line] => 157 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for Method 'asStdClass' + ) + + [18] => Array + ( + [line] => 165 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for Method 'setName' + ) + + [19] => Array + ( + [line] => 171 + [file] => /tests/data/api/animal/Animal.php + [message] => No docblock for element 'setBirthDate' + ) + + [20] => Array + ( + [line] => 180 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for Method 'getIntMaskProperty' + ) + + [21] => Array + ( + [line] => 189 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for Method 'setIntMaskOfProperty' + ) + + [22] => Array + ( + [line] => 199 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for Method 'setKeyOfProperty' + ) + + [23] => Array + ( + [line] => 208 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for Method 'getStatic' + ) + + [24] => Array + ( + [line] => 218 + [file] => /tests/data/api/animal/Animal.php + [message] => No short description for Method 'getSelfWithGenerics' + ) + + [25] => Array + ( + [line] => 38 + [file] => /tests/data/api/animal/Cat.php + [message] => No docblock for element 'methodWithoutDocAndTypeHints' + ) + + [26] => Array + ( + [line] => 46 + [file] => /tests/data/api/animal/Cat.php + [message] => No short description for Method 'methodWithInvalidReturnType' + ) + + [27] => Array + ( + [line] => 54 + [file] => /tests/data/api/animal/Cat.php + [message] => No short description for Method 'getSomePsalmType' + ) + + [28] => Array + ( + [line] => 62 + [file] => /tests/data/api/animal/Cat.php + [message] => No short description for Method 'getSomePhpStanType' + ) + + [29] => Array + ( + [line] => 70 + [file] => /tests/data/api/animal/Cat.php + [message] => No short description for Method 'getClassStringWithoutGenerics' + ) + + [30] => Array + ( + [line] => 78 + [file] => /tests/data/api/animal/Cat.php + [message] => No short description for Method 'getInterfaceStringWithoutGenerics' + ) + + [31] => Array + ( + [line] => 87 + [file] => /tests/data/api/animal/Cat.php + [message] => No short description for Method 'getInterfaceStringByClassString' + ) + + [32] => Array + ( + [line] => 95 + [file] => /tests/data/api/animal/Cat.php + [message] => No short description for Method 'methodWithTodoTag' + ) + + [33] => Array + ( + [line] => 32 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getThreeStringsArray' + ) + + [34] => Array + ( + [line] => 40 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'testOffsetAccess' + ) + + [35] => Array + ( + [line] => 48 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getNonEmptyList' + ) + + [36] => Array + ( + [line] => 56 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getListWithoutGenerics' + ) + + [37] => Array + ( + [line] => 64 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getNonEmptyListWithoutGenerics' + ) + + [38] => Array + ( + [line] => 72 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getClassWithTwoGenerics' + ) + + [39] => Array + ( + [line] => 79 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'methodWithInvalidReturnTag' + ) + + [40] => Array + ( + [line] => 86 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getArrayOfStatic' + ) + + [41] => Array + ( + [line] => 93 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getArrayWithStaticGeneric' + ) + + [42] => Array + ( + [line] => 100 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getIterableWithStaticGeneric' + ) + + [43] => Array + ( + [line] => 107 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getArrayShapeWithStaticGeneric' + ) + + [44] => Array + ( + [line] => 114 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getObjectShapeWithStaticGeneric' + ) + + [45] => Array + ( + [line] => 121 + [file] => /tests/data/api/animal/Dog.php + [message] => No short description for Method 'getStaticOrNull' + ) + + [46] => Array + ( + [line] => 11 + [file] => /tests/data/api/base/Component.php + [message] => No docblock for element 'yiiunit\apidoc\data\api\base\Component' + ) + + [47] => Array + ( + [line] => 24 + [file] => /tests/data/api/db/ActiveQuery.php + [message] => No short description for Method 'one' + ) + + [48] => Array + ( + [line] => 31 + [file] => /tests/data/api/db/ActiveQuery.php + [message] => No short description for Method 'all' + ) + + [49] => Array + ( + [line] => 11 + [file] => /tests/data/api/db/ActiveQueryInterface.php + [message] => No docblock for element 'yiiunit\apidoc\data\api\db\ActiveQueryInterface' + ) + + [50] => Array + ( + [line] => 13 + [file] => /tests/data/api/db/ActiveQueryInterface.php + [message] => No docblock for element 'one' + ) + + [51] => Array + ( + [line] => 14 + [file] => /tests/data/api/db/ActiveQueryInterface.php + [message] => No docblock for element 'all' + ) + + [52] => Array + ( + [line] => 11 + [file] => /tests/data/api/db/ActiveRecord.php + [message] => No docblock for element 'yiiunit\apidoc\data\api\db\ActiveRecord' + ) + + [53] => Array + ( + [line] => 11 + [file] => /tests/data/api/db/ActiveRecordInterface.php + [message] => No docblock for element 'yiiunit\apidoc\data\api\db\ActiveRecordInterface' + ) + +) diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__19.txt b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__19.txt new file mode 100644 index 0000000..f089874 --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__19.txt @@ -0,0 +1,38 @@ +Array +( + [0] => Array + ( + [line] => + [file] => + [message] => Using relative link (https://en.wikipedia.org/wiki/Dependency_injection) but repoUrl is not set. + ) + + [1] => Array + ( + [line] => 22 + [file] => /tests/data/api/animal/Cat.php + [message] => Invalid tag: @@method $first is true ? string : string[] methodWithInvalidReturnType2(bool $first) Will be ignored. + ) + + [2] => Array + ( + [line] => 46 + [file] => /tests/data/api/animal/Cat.php + [message] => Invalid tag: @@return $first is true ? string : string[] Incorrect conditional type (without parentheses). + ) + + [3] => Array + ( + [line] => 87 + [file] => /tests/data/api/animal/Cat.php + [message] => Undefined parameter documented: $ in getInterfaceStringByClassString(). + ) + + [4] => Array + ( + [line] => 79 + [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/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__2.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__2.html index a60f04c..10c3451 100644 --- a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__2.html +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__2.html @@ -44,9 +44,25 @@ Dog
yiiunit\apidoc\data\api\base +yiiunit\apidoc\data\api\db +yiiunit\apidoc\data\api\di +yiiunit\apidoc\data\api\web

Class yiiunit\apidoc\data\api\animal\Cat

@@ -83,6 +99,51 @@

Class yiiunit\apidoc\data\api\animal\Cat

+ +
+

PHPStan Types

+ + + + + + + + + + + + + + + +
NameValue
SomePhpStanType +string|array<string, mixed>|object +
+
+ + +
+

Psalm Types

+ + + + + + + + + + + + + + + +
NameValue
SomePsalmType +string|array<string, mixed>
+
+
@@ -109,12 +170,122 @@

Public Properties

integer animal age in seconds. yiiunit\apidoc\data\api\animal\Animal + + + $arrayShapeProperty + +array{someKey: string} + + yiiunit\apidoc\data\api\animal\Animal + + + $arrayWithParenthesesProperty + (yiiunit\apidoc\data\api\animal\Cat|yiiunit\apidoc\data\api\animal\Dog)[] + + yiiunit\apidoc\data\api\animal\Animal + + + $arrayWithoutParenthesesProperty + +yiiunit\apidoc\data\api\animal\Dog[]|yiiunit\apidoc\data\api\animal\Cat[] + + yiiunit\apidoc\data\api\animal\Animal $birthDate integer Animal birth date as a UNIX timestamp. yiiunit\apidoc\data\api\animal\Animal + + + $callableProperty + callable + + yiiunit\apidoc\data\api\animal\Animal + + + $closureProperty + callable + + yiiunit\apidoc\data\api\animal\Animal + + + $genericArrayWithKeyProperty + +array<array-key, string[]> + + yiiunit\apidoc\data\api\animal\Animal + + + $genericArrayWithoutKeyProperty + +string[] + + yiiunit\apidoc\data\api\animal\Animal + + + $intMaskOfProperty + +int-mask-of<1|2|4> + + yiiunit\apidoc\data\api\animal\Animal + + + $intMaskProperty + +int-mask<1, 2, 4> + + yiiunit\apidoc\data\api\animal\Animal + + + $intMaskReadProperty + +int-mask<1, 2, 4> + Some description (intMaskReadProperty). + yiiunit\apidoc\data\api\animal\Animal + + + $intRangeProperty + +int<0, max> + + yiiunit\apidoc\data\api\animal\Animal + + + $intersectionType + +yiiunit\apidoc\data\api\animal\Cat&yiiunit\apidoc\data\api\animal\Dog + + + yiiunit\apidoc\data\api\animal\Animal + + + $iterableProperty + +iterable<integer, string> + + yiiunit\apidoc\data\api\animal\Animal + + + $iterableWithoutKeyProperty + +iterable<string> + + yiiunit\apidoc\data\api\animal\Animal + + + $keyOfProperty + +key-of<self::COLORS> + + yiiunit\apidoc\data\api\animal\Animal + + + $keyOfWriteProperty + +key-of<self::COLORS> + Some description (keyOfWriteProperty). + yiiunit\apidoc\data\api\animal\Animal $name @@ -123,14 +294,23 @@

Public Properties

yiiunit\apidoc\data\api\animal\Animal - $propertyWithoutDoc - string + $objectShapeProperty + +object{someKey: string} yiiunit\apidoc\data\api\animal\Animal - $propertyWithoutDocAndTypeHint - + $valueOfAnnotationProperty + +value-of<self::COLORS> + Some description (valueOfAnnotationProperty). + yiiunit\apidoc\data\api\animal\Animal + + + $valueOfProperty + +value-of<self::COLORS> yiiunit\apidoc\data\api\animal\Animal @@ -157,19 +337,79 @@

Public Methods

+ asArray() + + yiiunit\apidoc\data\api\animal\Animal + + + asStdClass() + + yiiunit\apidoc\data\api\animal\Animal + + getAge() Returns animal age in seconds. yiiunit\apidoc\data\api\animal\Animal + + + getClassStringWithoutGenerics() + + yiiunit\apidoc\data\api\animal\Cat + + + getIntMaskProperty() + + yiiunit\apidoc\data\api\animal\Animal + + + getInterfaceStringByClassString() + + yiiunit\apidoc\data\api\animal\Cat + + + getInterfaceStringWithoutGenerics() + + yiiunit\apidoc\data\api\animal\Cat + + + getMixed() + + yiiunit\apidoc\data\api\animal\Animal + + + getSelfWithGenerics() + + yiiunit\apidoc\data\api\animal\Animal + + + getSomePhpStanType() + + yiiunit\apidoc\data\api\animal\Cat + + + getSomePsalmType() + + yiiunit\apidoc\data\api\animal\Cat getSomething() yiiunit\apidoc\data\api\animal\Animal + + + getStatic() + + yiiunit\apidoc\data\api\animal\Animal isOlder() Checks whether the animal is older than the specified time. yiiunit\apidoc\data\api\animal\Animal + + + methodWithInvalidReturnType() + + yiiunit\apidoc\data\api\animal\Cat methodWithTodoTag() @@ -190,6 +430,21 @@

Public Methods

setBirthDate() yiiunit\apidoc\data\api\animal\Animal + + + setIntMaskOfProperty() + + yiiunit\apidoc\data\api\animal\Animal + + + setKeyOfProperty() + + yiiunit\apidoc\data\api\animal\Animal + + + setName() + + yiiunit\apidoc\data\api\animal\Animal
@@ -285,9 +540,488 @@

Method Details

- + + + asArray() + + + public method + +
+ + + + + + + +
+publicAnimalDataasArray ( )
+ + + + +

+ +

+
+
+
+                publicfunctionasArray()
+{
+    return [
+        'name' => $this->name,
+        'birthDate' => $this->birthDate,
+    ];
+}
+
+            
+
+
+ +
+
+
+ + + + asStdClass() + + + public method + +
+ + + + + + + +
+publicobject{name: string, birthDate: integer}asStdClass ( )
+ + + + +

+ +

+
+
+
+                publicfunctionasStdClass()
+{
+    return (object) $this->asArray();
+}
+
+            
+
+
+ +
+
+
+ + + + getAge() + + + public method + +
+ + +
+

+ Defined in: + yiiunit\apidoc\data\api\animal\Animal::getAge()

+ +

Returns animal age in seconds.

+
+ + + + + + + + + + + +
+publicintegergetAge ( )
returninteger +

Animal age in seconds.

+
+ + + + +

+ +

+
+
+
+                publicfunctiongetAge()
+{
+    return time() - $this->birthDate;
+}
+
+            
+
+
+ +
+
+
+ + + + getClassStringWithoutGenerics() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicclass-stringgetClassStringWithoutGenerics ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetClassStringWithoutGenerics(): string
+{
+    return'';
+}
+
+            
+
+
+ +
+
+
+ + + + getIntMaskProperty() + + + public method + +
+ + + + + + + +
+publicint-mask<1, 2, 4>getIntMaskProperty ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetIntMaskProperty(): int
+{
+    return$this->intMaskProperty;
+}
+
+            
+
+
+ +
+
+
+ + + + getInterfaceStringByClassString() + + + public method + +
+ + +
+ +

+
+ + + + + + + + + + + +
+publicinterface-string<yiiunit\apidoc\data\api\animal\Cat>getInterfaceStringByClassString ( string$string )
$stringstring +
+ + + + +

+ +

+
+
+
+                publicfunctiongetInterfaceStringByClassString(string $string): string
+{
+    return'';
+}
+
+            
+
+
+ +
+
+
+ + + + getInterfaceStringWithoutGenerics() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicinterface-stringgetInterfaceStringWithoutGenerics ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetInterfaceStringWithoutGenerics(): string
+{
+    return'';
+}
+
+            
+
+
+ +
+
+
+ + + + getMixed() + + + public method + +
+ + + + + + + + + + + + + + +
+publicmixedgetMixed ( )
returnmixed +
+ + + + +
+
+
+ + + + getSelfWithGenerics() + + + public method + +
+ + + + + + + +
+publicself<yiiunit\apidoc\data\api\animal\Cat>getSelfWithGenerics ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetSelfWithGenerics()
+{
+    return$this;
+}
+
+            
+
+
+ +
+
+
+ + + + getSomePhpStanType() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicSomePhpStanTypegetSomePhpStanType ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetSomePhpStanType(): array
+{
+    return [];
+}
+
+            
+
+
+ +
+
+
+ + - getAge() + getSomePsalmType() public method @@ -296,42 +1030,30 @@

Method Details

-

- Defined in: - yiiunit\apidoc\data\api\animal\Animal::getAge()

-

Returns animal age in seconds.

+

+publicSomePsalmTypegetSomePsalmType ( ) - - - - - - - -
-publicintegergetAge ( )
returninteger -

Animal age in seconds.

-
+

-

-                publicfunctiongetAge()
+                publicfunctiongetSomePsalmType(): array
 {
-    return time() - $this->birthDate;
+    return [];
 }
 
             
@@ -397,6 +1119,56 @@

Method Details

+
+
+
+ + + + getStatic() + + + public static method + +
+ + + + + + + +
+public staticyiiunit\apidoc\data\api\animal\CatgetStatic ( )
+ + + + +

+ +

+
+
+
+                publicstaticfunctiongetStatic()
+{
+    return Yii::createObject([
+        'class' => get_called_class(),
+    ]);
+}
+
+            
+
+
+
@@ -449,6 +1221,59 @@

Method Details

{ return$this->getAge() > $date; } +
+ +
+
+ +
+
+
+ + + + methodWithInvalidReturnType() + + + public method + +
+ + +
+ +

+
+ + + + + + + + + + + +
+publicmixedmethodWithInvalidReturnType ( boolean$first )
$firstboolean +
+ + + + +

+ +

+
+
+
+                publicfunctionmethodWithInvalidReturnType(bool $first)
+{
+    return$first ? '' : [''];
+}
 
             
@@ -662,6 +1487,179 @@

Method Details

$this->birthDate = $birthDate; return$this; } + + +
+
+ +
+
+
+ + + + setIntMaskOfProperty() + + + public method + +
+ + + + + + + + + + + + + + +
+public$thissetIntMaskOfProperty ( int-mask-of<1|2|4>$newValue )
$newValue +int-mask-of<1|2|4> +
+ + + + +

+ +

+
+
+
+                publicfunctionsetIntMaskOfProperty(int $newValue): self
+{
+    $this->intMaskOfProperty = $newValue;
+    return$this;
+}
+
+            
+
+
+ +
+
+
+ + + + setKeyOfProperty() + + + public method + +
+ + + + + + + + + + + + + + +
+public$thissetKeyOfProperty ( key-of<self::COLORS>$newValue )
$newValue +key-of<self::COLORS> +
+ + + + +

+ +

+
+
+
+                publicfunctionsetKeyOfProperty(int $newValue): self
+{
+    $this->keyOfProperty = $newValue;
+    return$this;
+}
+
+            
+
+
+ +
+
+
+ + + + setName() + + + public method + +
+ + + + + + + + + + + + + + +
+public$thissetName ( string$newName )
$newNamestring +
+ + + + +

+ +

+
+
+
+                publicfunctionsetName(string $newName): self
+{
+    $this->name = trim($newName);
+    return$this;
+}
 
             
diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__3.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__3.html index 0199c67..023ee14 100644 --- a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__3.html +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__3.html @@ -44,9 +44,25 @@ Dog
yiiunit\apidoc\data\api\base +yiiunit\apidoc\data\api\db +yiiunit\apidoc\data\api\di +yiiunit\apidoc\data\api\web

Class yiiunit\apidoc\data\api\animal\Dog

@@ -82,6 +98,29 @@

Class yiiunit\apidoc\data\api\animal\Dog

+ +
+

PHPStan Types

+ + + + + + + + + + + + + + + +
NameValue
MyArray +array{foo: integer, bar: string}
+
+ +
@@ -108,12 +147,122 @@

Public Properties

integer animal age in seconds. yiiunit\apidoc\data\api\animal\Animal + + + $arrayShapeProperty + +array{someKey: string} + + yiiunit\apidoc\data\api\animal\Animal + + + $arrayWithParenthesesProperty + (yiiunit\apidoc\data\api\animal\Cat|yiiunit\apidoc\data\api\animal\Dog)[] + + yiiunit\apidoc\data\api\animal\Animal + + + $arrayWithoutParenthesesProperty + +yiiunit\apidoc\data\api\animal\Dog[]|yiiunit\apidoc\data\api\animal\Cat[] + + yiiunit\apidoc\data\api\animal\Animal $birthDate integer Animal birth date as a UNIX timestamp. yiiunit\apidoc\data\api\animal\Animal + + + $callableProperty + callable + + yiiunit\apidoc\data\api\animal\Animal + + + $closureProperty + callable + + yiiunit\apidoc\data\api\animal\Animal + + + $genericArrayWithKeyProperty + +array<array-key, string[]> + + yiiunit\apidoc\data\api\animal\Animal + + + $genericArrayWithoutKeyProperty + +string[] + + yiiunit\apidoc\data\api\animal\Animal + + + $intMaskOfProperty + +int-mask-of<1|2|4> + + yiiunit\apidoc\data\api\animal\Animal + + + $intMaskProperty + +int-mask<1, 2, 4> + + yiiunit\apidoc\data\api\animal\Animal + + + $intMaskReadProperty + +int-mask<1, 2, 4> + Some description (intMaskReadProperty). + yiiunit\apidoc\data\api\animal\Animal + + + $intRangeProperty + +int<0, max> + + yiiunit\apidoc\data\api\animal\Animal + + + $intersectionType + +yiiunit\apidoc\data\api\animal\Cat&yiiunit\apidoc\data\api\animal\Dog + + + yiiunit\apidoc\data\api\animal\Animal + + + $iterableProperty + +iterable<integer, string> + + yiiunit\apidoc\data\api\animal\Animal + + + $iterableWithoutKeyProperty + +iterable<string> + + yiiunit\apidoc\data\api\animal\Animal + + + $keyOfProperty + +key-of<self::COLORS> + + yiiunit\apidoc\data\api\animal\Animal + + + $keyOfWriteProperty + +key-of<self::COLORS> + Some description (keyOfWriteProperty). + yiiunit\apidoc\data\api\animal\Animal $name @@ -122,14 +271,23 @@

Public Properties

yiiunit\apidoc\data\api\animal\Animal - $propertyWithoutDoc - string + $objectShapeProperty + +object{someKey: string} yiiunit\apidoc\data\api\animal\Animal - $propertyWithoutDocAndTypeHint - + $valueOfAnnotationProperty + +value-of<self::COLORS> + Some description (valueOfAnnotationProperty). + yiiunit\apidoc\data\api\animal\Animal + + + $valueOfProperty + +value-of<self::COLORS> yiiunit\apidoc\data\api\animal\Animal @@ -156,14 +314,99 @@

Public Methods

+ asArray() + + yiiunit\apidoc\data\api\animal\Animal + + + asStdClass() + + yiiunit\apidoc\data\api\animal\Animal + + getAge() Returns animal age in seconds. yiiunit\apidoc\data\api\animal\Animal + + + getArrayOfStatic() + + yiiunit\apidoc\data\api\animal\Dog + + + getArrayShapeWithStaticGeneric() + + yiiunit\apidoc\data\api\animal\Dog + + + getArrayWithStaticGeneric() + + yiiunit\apidoc\data\api\animal\Dog + + + getClassWithTwoGenerics() + + yiiunit\apidoc\data\api\animal\Dog + + + getIntMaskProperty() + + yiiunit\apidoc\data\api\animal\Animal + + + getIterableWithStaticGeneric() + + yiiunit\apidoc\data\api\animal\Dog + + + getListWithoutGenerics() + + yiiunit\apidoc\data\api\animal\Dog + + + getMixed() + + yiiunit\apidoc\data\api\animal\Animal + + + getNonEmptyList() + + yiiunit\apidoc\data\api\animal\Dog + + + getNonEmptyListWithoutGenerics() + + yiiunit\apidoc\data\api\animal\Dog + + + getObjectShapeWithStaticGeneric() + + yiiunit\apidoc\data\api\animal\Dog + + + getSelfWithGenerics() + + yiiunit\apidoc\data\api\animal\Animal getSomething() yiiunit\apidoc\data\api\animal\Animal + + + getStatic() + + yiiunit\apidoc\data\api\animal\Animal + + + getStaticOrNull() + + yiiunit\apidoc\data\api\animal\Dog + + + getThreeStringsArray() + + yiiunit\apidoc\data\api\animal\Dog isOlder() @@ -184,6 +427,26 @@

Public Methods

setBirthDate() yiiunit\apidoc\data\api\animal\Animal + + + setIntMaskOfProperty() + + yiiunit\apidoc\data\api\animal\Animal + + + setKeyOfProperty() + + yiiunit\apidoc\data\api\animal\Animal + + + setName() + + yiiunit\apidoc\data\api\animal\Animal + + + testOffsetAccess() + + yiiunit\apidoc\data\api\animal\Dog
@@ -250,9 +513,9 @@

Method Details

- + - getAge() + asArray() public method @@ -263,40 +526,34 @@

Method Details

+publicAnimalDataasArray ( ) - - - - - - - -
-publicintegergetAge ( )
returninteger -

Animal age in seconds.

-
+

-

-                publicfunctiongetAge()
+                publicfunctionasArray()
 {
-    return time() - $this->birthDate;
+    return [
+        'name' => $this->name,
+        'birthDate' => $this->birthDate,
+    ];
 }
 
             
@@ -307,9 +564,9 @@

Method Details

- + - getSomething() + asStdClass() public method @@ -320,55 +577,44 @@

Method Details

+publicobject{name: string, birthDate: integer}asStdClass ( ) - - - - - - - - - - - - - - - - - - - - - - -
-publicintegergetSomething ( mixed$test, integer$test2, integer|string$test3 )
$testmixed -
$test2integer -
$test3 -integer|string - -
returninteger -
+ + +

+ +

+
+
+
+                publicfunctionasStdClass()
+{
+    return (object) $this->asArray();
+}
+
+            
+
+
- + - isOlder() + getAge() public method @@ -379,24 +625,813 @@

Method Details

Defined in: - yiiunit\apidoc\data\api\animal\Animal::isOlder()

+ yiiunit\apidoc\data\api\animal\Animal::getAge()

-

Checks whether the animal is older than the specified time.

+

Returns animal age in seconds.

+publicintegergetAge ( ) - - + + + - +
-publicbooleanisOlder ( integer$date )
$date
return integer -

Date as a UNIX timestamp.

+

Animal age in seconds.

+ + + + +

+ +

+
+
+
+                publicfunctiongetAge()
+{
+    return time() - $this->birthDate;
+}
+
+            
+
+
+ +
+
+
+ + + + getArrayOfStatic() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicyiiunit\apidoc\data\api\animal\Dog[]getArrayOfStatic ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetArrayOfStatic()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + getArrayShapeWithStaticGeneric() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicarray{someObject: yiiunit\apidoc\data\api\animal\Dog}getArrayShapeWithStaticGeneric ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetArrayShapeWithStaticGeneric()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + getArrayWithStaticGeneric() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicarray<string, static>getArrayWithStaticGeneric ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetArrayWithStaticGeneric()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + getClassWithTwoGenerics() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicyiiunit\apidoc\data\api\animal\Dog<yiiunit\apidoc\data\api\animal\Cat, yiiunit\apidoc\data\api\animal\Animal>getClassWithTwoGenerics ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetClassWithTwoGenerics()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + getIntMaskProperty() + + + public method + +
+ + + + + + + +
+publicint-mask<1, 2, 4>getIntMaskProperty ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetIntMaskProperty(): int
+{
+    return$this->intMaskProperty;
+}
+
+            
+
+
+ +
+
+
+ + + + getIterableWithStaticGeneric() + + + public method + +
+ + +
+ +

+
+ + + + +
+publiciterable<string, static>getIterableWithStaticGeneric ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetIterableWithStaticGeneric()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + getListWithoutGenerics() + + + public method + +
+ + +
+ +

+
+ + + + +
+publiclistgetListWithoutGenerics ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetListWithoutGenerics(): array
+{
+    return [];
+}
+
+            
+
+
+ +
+
+
+ + + + getMixed() + + + public method + +
+ + + + + + + + + + + + + + +
+publicmixedgetMixed ( )
returnmixed +
+ + + + +
+
+
+ + + + getNonEmptyList() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicnon-empty-list<array>getNonEmptyList ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetNonEmptyList(): array
+{
+    return [[]];
+}
+
+            
+
+
+ +
+
+
+ + + + getNonEmptyListWithoutGenerics() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicnon-empty-listgetNonEmptyListWithoutGenerics ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetNonEmptyListWithoutGenerics(): array
+{
+    return [];
+}
+
+            
+
+
+ +
+
+
+ + + + getObjectShapeWithStaticGeneric() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicarray{someObject: yiiunit\apidoc\data\api\animal\Dog}getObjectShapeWithStaticGeneric ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetObjectShapeWithStaticGeneric()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + getSelfWithGenerics() + + + public method + +
+ + + + + + + +
+publicself<yiiunit\apidoc\data\api\animal\Cat>getSelfWithGenerics ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetSelfWithGenerics()
+{
+    return$this;
+}
+
+            
+
+
+ +
+
+
+ + + + getSomething() + + + public method + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+publicintegergetSomething ( mixed$test, integer$test2, integer|string$test3 )
$testmixed +
$test2integer +
$test3 +integer|string + +
returninteger +
+ + + + +
+
+
+ + + + getStatic() + + + public static method + +
+ + + + + + + +
+public staticyiiunit\apidoc\data\api\animal\DoggetStatic ( )
+ + + + +

+ +

+
+
+
+                publicstaticfunctiongetStatic()
+{
+    return Yii::createObject([
+        'class' => get_called_class(),
+    ]);
+}
+
+            
+
+
+ +
+
+
+ + + + getStaticOrNull() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicyiiunit\apidoc\data\api\animal\Dog|nullgetStaticOrNull ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetStaticOrNull()
+{
+}
+
+            
+
+
+ +
+
+
+ + + + getThreeStringsArray() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicarray{string, string, string}getThreeStringsArray ( )
+ + + + +

+ +

+
+
+
+                publicfunctiongetThreeStringsArray(): array
+{
+    return ['one', 'two', 'three'];
+}
+
+            
+
+
+ +
+
+
+ + + + isOlder() + + + public method + +
+ + +
+

+ Defined in: + yiiunit\apidoc\data\api\animal\Animal::isOlder()

+ +

Checks whether the animal is older than the specified time.

+
+ + + + + + + + + + +
+publicbooleanisOlder ( integer$date )
$dateinteger +

Date as a UNIX timestamp.

+
@@ -570,6 +1605,224 @@

Method Details

$this->birthDate = $birthDate; return$this; } +
+ +
+
+ +
+
+
+ + + + setIntMaskOfProperty() + + + public method + +
+ + + + + + + + + + + + + + +
+public$thissetIntMaskOfProperty ( int-mask-of<1|2|4>$newValue )
$newValue +int-mask-of<1|2|4> +
+ + + + +

+ +

+
+
+
+                publicfunctionsetIntMaskOfProperty(int $newValue): self
+{
+    $this->intMaskOfProperty = $newValue;
+    return$this;
+}
+
+            
+
+
+ +
+
+
+ + + + setKeyOfProperty() + + + public method + +
+ + + + + + + + + + + + + + +
+public$thissetKeyOfProperty ( key-of<self::COLORS>$newValue )
$newValue +key-of<self::COLORS> +
+ + + + +

+ +

+
+
+
+                publicfunctionsetKeyOfProperty(int $newValue): self
+{
+    $this->keyOfProperty = $newValue;
+    return$this;
+}
+
+            
+
+
+ +
+
+
+ + + + setName() + + + public method + +
+ + + + + + + + + + + + + + +
+public$thissetName ( string$newName )
$newNamestring +
+ + + + +

+ +

+
+
+
+                publicfunctionsetName(string $newName): self
+{
+    $this->name = trim($newName);
+    return$this;
+}
+
+            
+
+
+ +
+
+
+ + + + testOffsetAccess() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicMyArray["bar"]testOffsetAccess ( )
+ + + + +

+ +

+
+
+
+                publicfunctiontestOffsetAccess(): string
+{
+    return'';
+}
 
             
diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__4.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__4.html index d15c360..5c7f3d7 100644 --- a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__4.html +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__4.html @@ -10,7 +10,7 @@ - Component, yiiunit\apidoc\data\api\base\Component - Yii Framework 2.0 API Documentation + Action, yiiunit\apidoc\data\api\base\Action - Yii Framework 2.0 API Documentation @@ -44,12 +44,28 @@ Dog
yiiunit\apidoc\data\api\base +yiiunit\apidoc\data\api\db +yiiunit\apidoc\data\api\di +yiiunit\apidoc\data\api\web
-

Class yiiunit\apidoc\data\api\base\Component

+

Class yiiunit\apidoc\data\api\base\Action

@@ -61,21 +77,22 @@

Class yiiunit\apidoc\data\api\base\Component

Inheritance -yiiunit\apidoc\data\api\base\Component - - -Subclasses -yiiunit\apidoc\data\api\base\Controller + +yiiunit\apidoc\data\api\base\Action » +yiiunit\apidoc\data\api\base\Component + - +
-

+

Action is the base class for all controller action classes.

+ + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__5.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__5.html index ddc95eb..bdf9335 100644 --- a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__5.html +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__5.html @@ -10,7 +10,7 @@ - Controller, yiiunit\apidoc\data\api\base\Controller - Yii Framework 2.0 API Documentation + Application, yiiunit\apidoc\data\api\base\Application - Yii Framework 2.0 API Documentation @@ -44,14 +44,31 @@ Dog
yiiunit\apidoc\data\api\base +yiiunit\apidoc\data\api\db +yiiunit\apidoc\data\api\di +yiiunit\apidoc\data\api\web
-

Class yiiunit\apidoc\data\api\base\Controller

+

Abstract Class yiiunit\apidoc\data\api\base\Application

@@ -61,28 +78,87 @@

Class yiiunit\apidoc\data\api\base\Controller

- +
Inheritance -yiiunit\apidoc\data\api\base\Controller » -yiiunit\apidoc\data\api\base\Component -yiiunit\apidoc\data\api\base\Application
-

Controller is the base class for classes containing controller logic.

+

Application is the base class for all application classes.

+ + +
+

Public Properties

+ +

Hide inherited properties

+ + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescriptionDefined By
$requestedAction +yiiunit\apidoc\data\api\base\Action<yiiunit\apidoc\data\api\base\Controller>|null +The requested Action.yiiunit\apidoc\data\api\base\Application
+
+ + +

Property Details

+ +
+

Hide inherited properties

+ +
+
+ + + + $requestedAction + + public property + +
+ + +
+ +

The requested Action. If null, it means the request cannot be resolved into an action.

+
+ + + + +
+
diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__5.txt b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__5.txt deleted file mode 100644 index f688c1b..0000000 --- a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__5.txt +++ /dev/null @@ -1,66 +0,0 @@ -Array -( - [0] => Array - ( - [line] => 41 - [file] => /tests/data/api/animal/Animal.php - [message] => No docblock for element '$propertyWithoutDoc' - ) - - [1] => Array - ( - [line] => 41 - [file] => /tests/data/api/animal/Animal.php - [message] => No short description for element '$propertyWithoutDoc' - ) - - [2] => Array - ( - [line] => 43 - [file] => /tests/data/api/animal/Animal.php - [message] => No docblock for element '$propertyWithoutDocAndTypeHint' - ) - - [3] => Array - ( - [line] => 43 - [file] => /tests/data/api/animal/Animal.php - [message] => No short description for element '$propertyWithoutDocAndTypeHint' - ) - - [4] => Array - ( - [line] => 70 - [file] => /tests/data/api/animal/Animal.php - [message] => No docblock for element 'setBirthDate' - ) - - [5] => Array - ( - [line] => 32 - [file] => /tests/data/api/animal/Cat.php - [message] => No docblock for element 'methodWithoutDocAndTypeHints' - ) - - [6] => Array - ( - [line] => 40 - [file] => /tests/data/api/animal/Cat.php - [message] => No short description for Method 'methodWithTodoTag' - ) - - [7] => Array - ( - [line] => 30 - [file] => /tests/data/api/animal/Dog.php - [message] => No short description for Method 'methodWithInvalidReturnTag' - ) - - [8] => Array - ( - [line] => 11 - [file] => /tests/data/api/base/Component.php - [message] => No docblock for element 'yiiunit\apidoc\data\api\base\Component' - ) - -) diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__6.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__6.html new file mode 100644 index 0000000..2175616 --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__6.html @@ -0,0 +1,364 @@ + + + + + + + + + + + + + Behavior, yiiunit\apidoc\data\api\base\Behavior - Yii Framework 2.0 API Documentation + + + +
+ + + + +
+ +
+

Class yiiunit\apidoc\data\api\base\Behavior

+ + + + + + + + + + + +
Inheritanceyiiunit\apidoc\data\api\base\Behavior
+ +
+

Behavior is the base class for all behavior classes.

+

A behavior can be used to enhance the functionality of an existing component without modifying its code. +In particular, it can "inject" its own methods and properties into the component +and make them directly accessible via the component. It can also respond to the events triggered in the component +and thus intercept the normal code execution.

+ +
+ + + + + + + +
+

Public Properties

+ +

Hide inherited properties

+ + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescriptionDefined By
$owner +yiiunit\apidoc\data\api\base\Component|null +The owner of this behavioryiiunit\apidoc\data\api\base\Behavior
+
+ + + +
+

Public Methods

+ +

Hide inherited methods

+ + + + + + + + + + + + + + + + + + +
MethodDescriptionDefined By
attach()Attaches the behavior object to the component.yiiunit\apidoc\data\api\base\Behavior
+
+ + + + + + +

Property Details

+ +
+

Hide inherited properties

+ +
+
+ + + + $owner + + public property + +
+ + +
+ +

The owner of this behavior

+
+ + + + +
+
+ +

Method Details

+ +
+

Hide inherited methods

+ +
+
+ + + + attach() + + + public method + +
+ + +
+ +

Attaches the behavior object to the component.

+
+ + + + + + + + + + + +
+publicmixedattach ( yiiunit\apidoc\data\api\base\Component$owner )
$owneryiiunit\apidoc\data\api\base\Component +

The component that this behavior is to be attached to.

+
+ + + + +

+ +

+
+
+
+                publicfunctionattach($owner)
+{
+    $this->owner = $owner;
+}
+
+            
+
+
+ +
+
+
+
+ + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__6.txt b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__6.txt deleted file mode 100644 index f939d78..0000000 --- a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__6.txt +++ /dev/null @@ -1,10 +0,0 @@ -Array -( - [0] => Array - ( - [line] => 30 - [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/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__7.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__7.html new file mode 100644 index 0000000..4b1fffa --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__7.html @@ -0,0 +1,218 @@ + + + + + + + + + + + + + Component, yiiunit\apidoc\data\api\base\Component - Yii Framework 2.0 API Documentation + + + +
+ + + + + + + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__8.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__8.html new file mode 100644 index 0000000..4ddba90 --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__8.html @@ -0,0 +1,215 @@ + + + + + + + + + + + + + Controller, yiiunit\apidoc\data\api\base\Controller - Yii Framework 2.0 API Documentation + + + +
+ + + + + + + + + +
+ + + + + + diff --git a/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__9.html b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__9.html new file mode 100644 index 0000000..9d845e0 --- /dev/null +++ b/tests/commands/__snapshots__/ApiControllerTest__testGenerateBootstrap__9.html @@ -0,0 +1,498 @@ + + + + + + + + + + + + + ActiveQuery, yiiunit\apidoc\data\api\db\ActiveQuery - Yii Framework 2.0 API Documentation + + + +
+ + + + +
+ +
+

Class yiiunit\apidoc\data\api\db\ActiveQuery

+ + + + + + + + + + + + + + + +
Inheritanceyiiunit\apidoc\data\api\db\ActiveQuery
Implementsyiiunit\apidoc\data\api\db\ActiveQueryInterface
+ +
+

ActiveQuery represents a DB query associated with an Active Record class.

+ +
+ + + + + + + + + + + + + + + + +

Method Details

+ +
+

Hide inherited methods

+ +
+
+ + + + all() + + + public method + +
+ + +
+ +

+
+ + + + +
+public(yiiunit\apidoc\data\api\db\ActiveRecord|array)[]all ( )
+ + + + +

+ +

+
+
+
+                publicfunctionall()
+{
+}
+
+            
+
+
+ +
+ +
+
+ + + + batch() + + + public method + +
+ + +
+ +

+
+ + + + + + + + + + + + + + + + + + + + + +
+publicyiiunit\apidoc\data\api\db\BatchQueryResult<integer, (yiiunit\apidoc\data\api\db\ActiveRecord|array)[]>batch ( mixed$batchSize, mixed$db )
$batchSizemixed +
$dbmixed +
return +yiiunit\apidoc\data\api\db\BatchQueryResult<integer, (yiiunit\apidoc\data\api\db\ActiveRecord|array)[]> +
+ + + + +
+
+
+ + + + each() + + + public method + +
+ + +
+ +

+
+ + + + + + + + + + + + + + + + + + + + + +
+publicyiiunit\apidoc\data\api\db\BatchQueryResult<integer, yiiunit\apidoc\data\api\db\ActiveRecord|array>each ( mixed$batchSize, mixed$db )
$batchSizemixed +
$dbmixed +
return +yiiunit\apidoc\data\api\db\BatchQueryResult<integer, yiiunit\apidoc\data\api\db\ActiveRecord|array> +
+ + + + +
+
+
+ + + + one() + + + public method + +
+ + +
+ +

+
+ + + + +
+publicyiiunit\apidoc\data\api\db\ActiveRecord|array|nullone ( )
+ + + + +

+ +

+
+
+
+                publicfunctionone()
+{
+}
+
+            
+
+
+ +
+
+
+
+ + + + +
+ + + + + + diff --git a/tests/data/api/animal/Animal.php b/tests/data/api/animal/Animal.php index 0a4c4ce..83fb6cf 100644 --- a/tests/data/api/animal/Animal.php +++ b/tests/data/api/animal/Animal.php @@ -1,4 +1,5 @@ $valueOfAnnotationProperty Some description (valueOfAnnotationProperty). + * @property-read int-mask<1, 2, 4> $intMaskReadProperty Some description (intMaskReadProperty). + * @property-write key-of $keyOfWriteProperty Some description (keyOfWriteProperty). + * + * @phpstan-property int-mask<1, 2, 4> $intMaskPhpStanProperty Ignored annotation. + * @psalm-property int-mask<1, 2, 4> $intMaskPsalmProperty Ignored annotation. + * @phan-property int-mask<1, 2, 4> $intMaskPhanProperty Ignored annotation. + * + * @phpstan-type AnimalData array{name: string, birthDate: int} * * @author Paul Klimov * @since 1.0 @@ -37,10 +50,70 @@ abstract class Animal extends BaseObject * @var int animal birth date as a UNIX timestamp. */ public $birthDate; - - public string $propertyWithoutDoc = ''; - - public $propertyWithoutDocAndTypeHint = ''; + /** + * @var (Cat|Dog)[] + */ + public $arrayWithParenthesesProperty; + /** + * @var Dog[]|Cat[] + */ + public $arrayWithoutParenthesesProperty; + /** + * @var int<0, max> + */ + public $intRangeProperty; + /** + * @var array{someKey: string} + */ + public $arrayShapeProperty; + /** + * @var object{someKey: string} + */ + public $objectShapeProperty; + /** + * @var iterable + */ + public $iterableProperty; + /** + * @var iterable + */ + public $iterableWithoutKeyProperty; + /** + * @var array + */ + public $genericArrayWithoutKeyProperty; + /** + * @var array> + */ + public $genericArrayWithKeyProperty; + /** + * @var callable(mixed): bool + */ + public $callableProperty; + /** + * @var \Closure(mixed): bool + */ + public $closureProperty; + /** + * @var Cat&Dog + */ + public $intersectionType; + /** + * @var int-mask<1, 2, 4> + */ + public $intMaskProperty; + /** + * @var int-mask-of<1|2|4> + */ + public $intMaskOfProperty; + /** + * @var value-of + */ + public $valueOfProperty; + /** + * @var key-of + */ + public $keyOfProperty; /** * Renders animal description. @@ -67,9 +140,83 @@ public function isOlder($date) return $this->getAge() > $date; } + /** + * @return AnimalData + */ + public function asArray() + { + return [ + 'name' => $this->name, + 'birthDate' => $this->birthDate, + ]; + } + + /** + * @return object{name: string, birthDate: int} + */ + public function asStdClass() + { + return (object) $this->asArray(); + } + + /** + * @return $this + */ + public function setName(string $newName): self + { + $this->name = trim($newName); + return $this; + } + public function setBirthDate(int $birthDate): self { $this->birthDate = $birthDate; return $this; } + + /** + * @return int-mask<1, 2, 4> + */ + public function getIntMaskProperty(): int + { + return $this->intMaskProperty; + } + + /** + * @param int-mask-of<1|2|4> $newValue + * @return $this + */ + public function setIntMaskOfProperty(int $newValue): self + { + $this->intMaskOfProperty = $newValue; + return $this; + } + + /** + * @param key-of $newValue + * @return $this + */ + public function setKeyOfProperty(int $newValue): self + { + $this->keyOfProperty = $newValue; + return $this; + } + + /** + * @return static + */ + public static function getStatic() + { + return Yii::createObject([ + 'class' => get_called_class(), + ]); + } + + /** + * @return self + */ + public function getSelfWithGenerics() + { + return $this; + } } diff --git a/tests/data/api/animal/Cat.php b/tests/data/api/animal/Cat.php index 5ddb66a..796e2ad 100644 --- a/tests/data/api/animal/Cat.php +++ b/tests/data/api/animal/Cat.php @@ -1,4 +1,5 @@ ) + * @phpstan-type SomePhpStanType (string|array|object) + * * @author Paul Klimov * @since 1.1 */ @@ -34,6 +40,55 @@ public function methodWithoutDocAndTypeHints($param) return $param; } + /** + * @return $first is true ? string : string[] Incorrect conditional type (without parentheses) + */ + public function methodWithInvalidReturnType(bool $first) + { + return $first ? '' : ['']; + } + + /** + * @return SomePsalmType + */ + public function getSomePsalmType(): array + { + return []; + } + + /** + * @return SomePhpStanType + */ + public function getSomePhpStanType(): array + { + return []; + } + + /** + * @return class-string + */ + public function getClassStringWithoutGenerics(): string + { + return ''; + } + + /** + * @return interface-string + */ + public function getInterfaceStringWithoutGenerics(): string + { + return ''; + } + + /** + * @param class-string + * @return interface-string + */ + public function getInterfaceStringByClassString(string $string): string + { + return ''; + } + /** * @todo Some description for todo tag. */ diff --git a/tests/data/api/animal/Dog.php b/tests/data/api/animal/Dog.php index 6752a83..7a59430 100644 --- a/tests/data/api/animal/Dog.php +++ b/tests/data/api/animal/Dog.php @@ -10,6 +10,8 @@ /** * Dog represents a dog animal. * + * @phpstan-type MyArray array{foo: int, bar: string} + * * @author Paul Klimov * @since 1.1 */ @@ -24,10 +26,99 @@ public function render() return 'This is a dog'; } + /** + * @return array{string, string, string} + */ + public function getThreeStringsArray(): array + { + return ['one', 'two', 'three']; + } + + /** + * @return MyArray['bar'] + */ + public function testOffsetAccess(): string + { + return ''; + } + + /** + * @return non-empty-list + */ + public function getNonEmptyList(): array + { + return [[]]; + } + + /** + * @return list + */ + public function getListWithoutGenerics(): array + { + return []; + } + + /** + * @return non-empty-list + */ + public function getNonEmptyListWithoutGenerics(): array + { + return []; + } + + /** + * @return Dog + */ + public function getClassWithTwoGenerics() + { + } + /** * @return invalid-type */ public function methodWithInvalidReturnTag() { } + + /** + * @return static[] + */ + public function getArrayOfStatic() + { + } + + /** + * @return array + */ + public function getArrayWithStaticGeneric() + { + } + + /** + * @return iterable + */ + public function getIterableWithStaticGeneric() + { + } + + /** + * @return array{someObject: static} + */ + public function getArrayShapeWithStaticGeneric() + { + } + + /** + * @return array{someObject: static} + */ + public function getObjectShapeWithStaticGeneric() + { + } + + /** + * @return static|null + */ + public function getStaticOrNull() + { + } } diff --git a/tests/data/api/base/Action.php b/tests/data/api/base/Action.php new file mode 100644 index 0000000..9c338d2 --- /dev/null +++ b/tests/data/api/base/Action.php @@ -0,0 +1,18 @@ +|null the requested Action. If null, it means the request cannot be resolved into an action. + */ + public $requestedAction; +} diff --git a/tests/data/api/base/Behavior.php b/tests/data/api/base/Behavior.php new file mode 100644 index 0000000..da818a7 --- /dev/null +++ b/tests/data/api/base/Behavior.php @@ -0,0 +1,36 @@ +owner = $owner; + } +} diff --git a/tests/data/api/db/ActiveQuery.php b/tests/data/api/db/ActiveQuery.php new file mode 100644 index 0000000..b6d822c --- /dev/null +++ b/tests/data/api/db/ActiveQuery.php @@ -0,0 +1,34 @@ + : static>) : static) asArray($value = true) + * @method BatchQueryResult batch($batchSize = 100, $db = null) + * @method BatchQueryResult each($batchSize = 100, $db = null) + */ +class ActiveQuery implements ActiveQueryInterface +{ + /** + * @return T|null + */ + public function one() + { + } + + /** + * @return T[] + */ + public function all() + { + } +} diff --git a/tests/data/api/db/ActiveQueryInterface.php b/tests/data/api/db/ActiveQueryInterface.php new file mode 100644 index 0000000..9150602 --- /dev/null +++ b/tests/data/api/db/ActiveQueryInterface.php @@ -0,0 +1,15 @@ + all($db = null) See [[ActiveQueryInterface::all()]] for more info. + * + * @property class-string $modelClass + * @property (int|string)[] $someProperty + */ +trait ActiveRelationTrait +{ +} diff --git a/tests/data/api/db/BatchQueryResult.php b/tests/data/api/db/BatchQueryResult.php new file mode 100644 index 0000000..951ccfc --- /dev/null +++ b/tests/data/api/db/BatchQueryResult.php @@ -0,0 +1,67 @@ + + */ +class BatchQueryResult extends Component implements \Iterator +{ + /** + * Resets the iterator to the initial state. + * This method is required by the interface [[\Iterator]]. + */ + #[\ReturnTypeWillChange] + public function rewind() + { + } + + /** + * Moves the internal pointer to the next dataset. + * This method is required by the interface [[\Iterator]]. + */ + #[\ReturnTypeWillChange] + public function next() + { + } + + /** + * Returns the index of the current dataset. + * This method is required by the interface [[\Iterator]]. + * @return int the index of the current row. + */ + #[\ReturnTypeWillChange] + public function key() + { + } + + /** + * Returns the current dataset. + * This method is required by the interface [[\Iterator]]. + * @return mixed the current dataset. + */ + #[\ReturnTypeWillChange] + public function current() + { + } + + /** + * Returns whether there is a valid dataset at the current position. + * This method is required by the interface [[\Iterator]]. + * @return bool whether there is a valid dataset at the current position. + */ + #[\ReturnTypeWillChange] + public function valid() + { + } +} diff --git a/tests/data/api/di/Container.php b/tests/data/api/di/Container.php new file mode 100644 index 0000000..35c279f --- /dev/null +++ b/tests/data/api/di/Container.php @@ -0,0 +1,33 @@ + $class the class name, or an alias name (e.g. `foo`). + * @param array $params a list of constructor parameter values. Use one of two definitions: + * - Parameters as name-value pairs, for example: `['posts' => PostRepository::class]`. + * - Parameters in the order they appear in the constructor declaration. If you want to skip some parameters, + * you should index the remaining ones with the integers that represent their positions in the constructor + * parameter list. + * Dependencies indexed by name and by position in the same array are not allowed. + * @param array $config a list of name-value pairs that will be used to initialize the object properties. + * @return ($class is class-string ? T : object) an instance of the requested class. + */ + public function get($class, $params = [], $config = []) + { + } +} diff --git a/tests/data/api/web/AssetBundle.php b/tests/data/api/web/AssetBundle.php new file mode 100644 index 0000000..22125c0 --- /dev/null +++ b/tests/data/api/web/AssetBundle.php @@ -0,0 +1,24 @@ +assertSame($expectedResult, $result); + $result = TypeHelper::getTypesByAggregatedType($type); + $this->assertEquals($expectedResult, $result); } /** - * @return array + * @return array */ - public static function provideSplitTypeData(): array + public static function provideGetTypesByAggregatedTypeData(): array { return [ - 'without type' => [ - null, - [], - ], - 'string' => [ - new String_(), - ['string'], - ], - 'integer' => [ - new Integer(), - ['int'], + 'compound' => [ + new Compound([new String_(), new Integer(), new Float_()]), + [new String_(), new Integer(), new Float_()], ], - // TODO: Fix this test when the intersection type handling is fixed. 'intersection' => [ - new Intersection([new Object_(new Fqsen('\Exception')), new Object_(new Fqsen('\SomeException'))]), - ['\Exception', '\SomeException'], + new Intersection([new Object_(new Fqsen('\\A')), new Object_(new Fqsen('\\B'))]), + [new Object_(new Fqsen('\\A')), new Object_(new Fqsen('\\B'))], ], - 'compound' => [ - new Compound([new Object_(new Fqsen('\Exception')), new Object_(new Fqsen('\SomeException'))]), - ['\Exception', '\SomeException'], + ]; + } + + /** + * @dataProvider provideGetPossibleTypesByConditionalTypeData + * + * @param ConditionalForParameter|Conditional $type + * @param Type[] $expectedResult + */ + public function testGetPossibleTypesByConditionalType(Type $type, array $expectedResult): void + { + $result = TypeHelper::getPossibleTypesByConditionalType($type); + $this->assertEquals($expectedResult, $result); + } + + /** + * @return array + */ + public static function provideGetPossibleTypesByConditionalTypeData(): array + { + return [ + 'basic' => [ + new ConditionalForParameter( + false, + 'first', + new True_(), + new String_(), + new Array_(new String_()), + ), + [new String_(), new Array_(new String_())], + ], + 'with compound' => [ + new ConditionalForParameter( + false, + 'first', + new True_(), + new Compound([new String_(), new Integer()]), + new String_(), + ), + [new String_(), new Integer()], ], - 'array key' => [ - new ArrayKey(), - ['string', 'int'], + 'nested' => [ + new ConditionalForParameter( + false, + 'value', + new True_(), + new Conditional( + false, + new Object_(new Fqsen('\\T')), + new Array_(), + new Static_(new Object_(new Fqsen('\\T'))), + new Static_(new Array_(new Mixed_(), new String_())), + ), + new Static_(new Object_(new Fqsen('\\T'))), + ), + [ + new Static_(new Object_(new Fqsen('\\T'))), + new Static_(new Array_(new Mixed_(), new String_())), + ], ], ]; }