create($docblock); $summary = 'This is a short (...) description.'; $description = 'This is a long description.'; $this->assertInstanceOf(DocBlock::class, $phpdoc); $this->assertSame($summary, $phpdoc->getSummary()); $this->assertSame($description, $phpdoc->getDescription()->render()); $this->assertCount(1, $phpdoc->getTags()); $this->assertTrue($phpdoc->hasTag('return')); } public function testInterpretingASimpleDocBlock(): void { /** @var DocBlock $docblock */ $docblock; /** @var string $summary */ $summary; /** @var Description $description */ $description; include(__DIR__ . '/../../examples/01-interpreting-a-simple-docblock.php'); $descriptionText = <<assertInstanceOf(DocBlock::class, $docblock); $this->assertSame('This is an example of a summary.', $summary); $this->assertInstanceOf(Description::class, $description); $this->assertSame( str_replace( PHP_EOL, "\n", $descriptionText ), $description->render() ); $this->assertEmpty($docblock->getTags()); } public function testInterpretingTags(): void { /** @var DocBlock $docblock */ $docblock; /** @var boolean $hasSeeTag */ $hasSeeTag; /** @var Tag[] $tags */ $tags; /** @var See[] $seeTags */ $seeTags; include(__DIR__ . '/../../examples/02-interpreting-tags.php'); $this->assertTrue($hasSeeTag); $this->assertCount(1, $tags); $this->assertCount(1, $seeTags); $this->assertInstanceOf(See::class, $tags[0]); $this->assertInstanceOf(See::class, $seeTags[0]); $seeTag = $seeTags[0]; $this->assertSame('\\' . StandardTagFactory::class, (string)$seeTag->getReference()); $this->assertSame('', (string)$seeTag->getDescription()); } public function testDescriptionsCanEscapeAtSignsAndClosingBraces(): void { /** @var string $docComment */ $docComment; /** @var DocBlock $docblock */ $docblock; /** @var Description $description */ $description; /** @var string $receivedDocComment */ $receivedDocComment; /** @var string $foundDescription */ $foundDescription; include(__DIR__ . '/../../examples/playing-with-descriptions/02-escaping.php'); $this->assertSame( str_replace( PHP_EOL, "\n", <<<'DESCRIPTION' You can escape the @-sign by surrounding it with braces, for example: @. And escape a closing brace within an inline tag by adding an opening brace in front of it like this: }. Here are example texts where you can see how they could be used in a real life situation: This is a text with an {@internal inline tag where a closing brace (}) is shown}. Or an {@internal inline tag with a literal {@link} in it}. Do note that an {@internal inline tag that has an opening brace ({) does not break out}. DESCRIPTION ), $foundDescription ); } public function testMultilineTags(): void { $docComment = << \$store */ DOC; $factory = DocBlockFactory::createInstance(); $docblock = $factory->create($docComment); self::assertEquals( [ new Param( 'store', new Array_( new String_(), new Integer() ), false, new Description(''), ), ], $docblock->getTags() ); } public function testMethodRegression(): void { $docComment = <<create($docComment); self::assertEquals( [ new Method( 'setInteger', [], new Void_(), false, new Description(''), false, [ new MethodParameter('integer', new Integer()) ] ), ], $docblock->getTags() ); } public function testInvalidTypeParamResultsInInvalidTag(): void { $docComment = ' /** * This is an example of a summary. * * @param array\Foo> $test */ '; $factory = DocBlockFactory::createInstance(); $docblock = $factory->create($docComment); self::assertEquals( [ InvalidTag::create( 'array\Foo> $test', 'param', )->withError( new \InvalidArgumentException( 'Could not find type in array\Foo> $test, please check for malformed notations') ), ], $docblock->getTags() ); } public function testConstantReferenceTypes(): void { $docComment = <<create($docComment); self::assertEquals( [ new Return_(new ConstExpression(new Self_(), 'STATUS_*'), new Description('')), ], $docblock->getTags() ); } public function testRegressionWordpressDocblocks(): void { $docComment = <<create($docComment); self::assertEquals( new DocBlock( 'Install a package.', new Description( 'Copies the contents of a package from a source directory, and installs them in' . PHP_EOL . 'a destination directory. Optionally removes the source. It can also optionally' . PHP_EOL . 'clear out the destination folder if it already exists.' ), [ new Since('2.8.0', new Description('')), new Since('6.2.0', new Description('Use move_dir() instead of copy_dir() when possible.')), new Generic( 'global', new Description('WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.') ), new Generic( 'global', new Description('array $wp_theme_directories') ), new Param( 'args', new Compound([new Array_(new Mixed_()), new String_()]), false, new Description( '{' . "\n" . ' Optional. Array or string of arguments for installing a package. Default empty array.' . "\n" . "\n" . ' @type string $source Required path to the package source. Default empty.' . "\n" . ' @type string $destination Required path to a folder to install the package in.' . "\n" . ' Default empty.' . "\n" . ' @type bool $clear_destination Whether to delete any files already in the destination' . "\n" . ' folder. Default false.' . "\n" . ' @type bool $clear_working Whether to delete the files from the working directory' . "\n" . ' after copying them to the destination. Default false.' . "\n" . ' @type bool $abort_if_destination_exists Whether to abort the installation if' . "\n" . ' the destination folder already exists. Default true.' . "\n" . ' @type array $hook_extra Extra arguments to pass to the filter hooks called by' . "\n" . ' WP_Upgrader::install_package(). Default empty array.' . "\n" . '}' ) ), new Return_( new Compound([new Array_(new Mixed_()), new Object_(new Fqsen('\WP_Error'))]), new Description('The result (also stored in `WP_Upgrader::$result`), or a WP_Error on failure.') ), ], new Context('\\') ), $docblock ); } public function testIndentationIsKept(): void { $docComment = <<create($docComment); self::assertEquals( new DocBlock( 'Registers the script module if no script module with that script module identifier has already been registered.', new Description( '' ), [ new Since('6.5.0', new Description('')), new Param( 'deps', new Array_(new Mixed_()), false, new Description("{ Optional. List of dependencies. @type string|array ...$0 { An array of script module identifiers of the dependencies of this script module. The dependencies can be strings or arrays. If they are arrays, they need an `id` key with the script module identifier, and can contain an `import` key with either `static` or `dynamic`. By default, dependencies that don't contain an `import` key are considered static. @type string \$id The script module identifier. @type string \$import Optional. Import type. May be either `static` or `dynamic`. Defaults to `static`. } }" ) ), ], new Context('\\') ), $docblock ); } public function testProcessTemplateTag(): void { $docComment = <<create($docComment); self::assertEquals( [ new Template( 'T', new Object_(new Fqsen('\\Type')), new Mixed_(), new Description('this is a description') ), new Template( 'TDefault', new Object_(new Fqsen('\\Type')), new Object_(new Fqsen('\\String_')), new Description('this is a description') ), ], $docblock->getTags() ); } }