diff --git a/src/Alias.php b/src/Alias.php index c983ac8..7a39d5f 100644 --- a/src/Alias.php +++ b/src/Alias.php @@ -11,6 +11,7 @@ namespace Barryvdh\LaravelIdeHelper; +use Barryvdh\LaravelIdeHelper\DocBlock\DocBlockBuilder; use Closure; use Illuminate\Config\Repository as ConfigRepository; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; @@ -18,7 +19,6 @@ use Illuminate\Support\Facades\Facade; use phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock\Serializer; use phpDocumentor\Reflection\DocBlock\Tags\Method as MethodTag; -use phpDocumentor\Reflection\DocBlockFactory; use phpDocumentor\Reflection\Types\Context; use ReflectionClass; use Throwable; @@ -87,11 +87,8 @@ class Alias //Create a DocBlock $this->phpdocContext = new Context($this->namespace, $this->classAliases); $reflector = new ReflectionClass($alias); - if ($reflector->getDocComment()) { - $this->phpdoc = (DocBlockFactory::createInstance())->create($reflector, $this->phpdocContext); - } else { - $this->phpdoc = new DocBlock('', null, [], $this->phpdocContext); - } + + $this->phpdoc = DocBlockBuilder::createFromReflector($reflector, $this->phpdocContext); } if ($facade === '\Illuminate\Database\Eloquent\Model') { @@ -440,18 +437,13 @@ class Alias // we can perform reflection on the class and // add in the original class DocBlock if (count($this->phpdoc->getTags()) === 0) { - $reflector = new ReflectionClass($this->root); - if ($reflector->getDocComment()) { - $this->phpdoc = (DocBlockFactory::createInstance())->create($reflector, $this->phpdocContext); - } else { - $this->phpdoc = new DocBlock('', null, [], $this->phpdocContext); - } + $this->phpdoc = DocBlockBuilder::createFromReflector($reflector, $this->phpdocContext); } } $this->removeDuplicateMethodsFromPhpDoc(); - return $serializer->getDocComment($this->phpdoc); + return $serializer->getDocComment($this->phpdoc->getDocBlock()); } /** diff --git a/src/Console/ModelsCommand.php b/src/Console/ModelsCommand.php index 55bc717..8e56b67 100644 --- a/src/Console/ModelsCommand.php +++ b/src/Console/ModelsCommand.php @@ -12,7 +12,7 @@ namespace Barryvdh\LaravelIdeHelper\Console; use Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface; -use Barryvdh\LaravelIdeHelper\TypeResolver\LocalFsqenResolver; +use Barryvdh\LaravelIdeHelper\DocBlock\DocBlockBuilder; use Composer\ClassMapGenerator\ClassMapGenerator; use Illuminate\Console\Command; use Illuminate\Contracts\Database\Eloquent\Castable; @@ -39,15 +39,10 @@ use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Str; -use phpDocumentor\Reflection\DocBlock; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\DocBlock\Serializer; -use phpDocumentor\Reflection\DocBlock\StandardTagFactory; use phpDocumentor\Reflection\DocBlock\Tag; -use phpDocumentor\Reflection\DocBlock\TagFactory; use phpDocumentor\Reflection\DocBlock\Tags\BaseTag; use phpDocumentor\Reflection\DocBlockFactory; -use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\ContextFactory; use ReflectionClass; use ReflectionNamedType; @@ -893,29 +888,25 @@ class ModelsCommand extends Command $reflection->getParentClass()->getInterfaceNames() ); $phpDocContext = (new ContextFactory())->createFromReflector($reflection); - $tagFactory = $this->getTagFactory(); + $phpdoc = DocBlockBuilder::createFromReflector($reflection, $phpDocContext); - $existingTags = []; - $tags = []; - $summary = $class; - $description = null; - - if ($reflection->getDocComment()) { - $phpdoc = (DocBlockFactory::createInstance())->create($reflection, $phpDocContext); - $existingTags = $phpdoc->getTags(); - if (!$this->reset || $this->keep_text) { - $summary = $phpdoc->getSummary(); - $description = $phpdoc->getDescription(); - } - - if (!$this->reset) { - $tags = $existingTags; + if ($this->reset) { + $phpdoc->clearTags(); + if (!$this->keep_text) { + $phpdoc->setSummary(''); + $phpdoc->setDescription(null); } } + // Set default summary to classname + if (!$phpdoc->getSummary()) { + $phpdoc->setSummary($class); + } + + $existingTags = $phpdoc->getTags(); $properties = []; $methods = []; - foreach ($tags as $tag) { + foreach ($phpdoc->getTags() as $tag) { $name = $tag->getName(); if ($name == 'property' || $name == 'property-read' || $name == 'property-write') { $properties[] = $tag->getVariableName(); @@ -944,7 +935,7 @@ class ModelsCommand extends Command $tagLine = trim("@{$attr} {$property['type']} {$name} {$property['comment']}"); - $tags[] = $tagFactory->create($tagLine, $phpDocContext); + $phpdoc->appendTagline($tagLine); } ksort($this->methods); @@ -958,13 +949,14 @@ class ModelsCommand extends Command if ($method['comment'] !== '') { $tagLine .= " {$method['comment']}"; } - $tags[] = $tagFactory->create($tagLine, $phpDocContext); + + $phpdoc->appendTagline($tagLine); } if ($this->write) { $eloquentClassNameInModel = $this->getClassNameInDestinationFile($reflection, 'Eloquent'); - $tags[] = $tagFactory->create('@mixin ' . $eloquentClassNameInModel, $phpDocContext); + $phpdoc->appendTagline('@mixin ' . $eloquentClassNameInModel); } if ($this->phpstorm_noinspections) { @@ -972,13 +964,12 @@ class ModelsCommand extends Command * Facades, Eloquent API * @see https://www.jetbrains.com/help/phpstorm/php-fully-qualified-name-usage.html */ - $tags[] = $tagFactory->create('@noinspection PhpFullyQualifiedNameUsageInspection', $phpDocContext); - + $phpdoc->appendTagline('@noinspection PhpFullyQualifiedNameUsageInspection'); /** * Relations, other models in the same namespace * @see https://www.jetbrains.com/help/phpstorm/php-unnecessary-fully-qualified-name.html */ - $tags[] = $tagFactory->create('@noinspection PhpUnnecessaryFullyQualifiedNameInspection', $phpDocContext); + $phpdoc->appendTagline('@noinspection PhpUnnecessaryFullyQualifiedNameInspection'); } $serializer = new Serializer(); @@ -989,31 +980,24 @@ class ModelsCommand extends Command return !($tag instanceof BaseTag) || !Str::startsWith($tag->getDescription(), 'IdeHelper'); }); + $phpdocMixin = DocBlockBuilder::create($phpdoc->getSummary(), $phpdoc->getDescription(), $mixinTags, $phpDocContext); $mixinClassName = "IdeHelper{$classname}"; - $mixinTags[] = $tagFactory->create("@mixin {$mixinClassName}", $phpDocContext); + $phpdocMixin->appendTagline("@mixin {$mixinClassName}"); - $tags = array_filter($tags, function (Tag $tag) { - return !($tag instanceof BaseTag) || !Str::startsWith($tag->getDescription(), 'IdeHelper'); - }); + foreach ($phpdoc->getTags() as $tag) { + if ($tag instanceof BaseTag && Str::startsWith($tag->getDescription(), 'IdeHelper')) { + $phpdoc->removeTag($tag); + } + } - $phpdocMixin = new DocBlock($summary ?: $class, $description, $mixinTags, $phpDocContext); - $mixinDocComment = $serializer->getDocComment($phpdocMixin); + $mixinDocComment = $serializer->getDocComment($phpdocMixin->getDocBlock()); // remove blank lines if there's no text if (!$phpdocMixin->getSummary()) { $mixinDocComment = preg_replace("/\s\*\s*\n/", '', $mixinDocComment); } } - $tags = collect($tags)->unique(function (Tag $tag) { - if (method_exists($tag, 'getVariableName')) { - return $tag->getName() . ' ' . $tag->getVariableName(); - } - return (string) $tag; - - })->toArray(); - - $phpdoc = new DocBlock($summary ?: '', $description, $tags, $phpDocContext); - $docComment = $serializer->getDocComment($phpdoc); + $docComment = $serializer->getDocComment($phpdoc->getDocBlock()); if ($this->write) { $modelDocComment = $this->write_mixin ? $mixinDocComment : $docComment; @@ -1050,18 +1034,6 @@ class ModelsCommand extends Command return $output . "{}\n}\n\n"; } - private function getTagFactory(): TagFactory - { - $fqsenResolver = new LocalFsqenResolver(); - $tagFactory = new StandardTagFactory($fqsenResolver); - $descriptionFactory = new DescriptionFactory($tagFactory); - - $tagFactory->addService($descriptionFactory); - $tagFactory->addService(new TypeResolver($fqsenResolver)); - - return $tagFactory; - } - /** * Get the parameters and format them correctly * diff --git a/src/DocBlock/DocBlockBuilder.php b/src/DocBlock/DocBlockBuilder.php new file mode 100644 index 0000000..984ce6b --- /dev/null +++ b/src/DocBlock/DocBlockBuilder.php @@ -0,0 +1,141 @@ +getDocComment()) { + $docblock = DocBlockFactory::createInstance()->create($doc, $context); + + return new static( + $docblock->getSummary(), + $docblock->getDescription(), + $docblock->getTags(), + $docblock->getContext() + ); + } + + return static::create('', null, [], $context); + } + + public static function create( + string $summary = '', + ?DocBlock\Description $description = null, + array $tags = null, + ?Context $context = null + ): static { + return new static($summary, $description, $tags, $context); + } + + public function getDocBlock(): DocBlock + { + $tags = collect($this->tags)->unique(function (Tag $tag) { + if (method_exists($tag, 'getVariableName')) { + return $tag->getName() . ' ' . $tag->getVariableName(); + } + return (string) $tag; + })->toArray(); + + return new DocBlock($this->summary, $this->description, $tags, $this->context); + } + + public function getSummary(): ?string + { + return $this->summary; + } + + public function setSummary(string $summary) + { + $this->summary = $summary; + } + + public function getDescription(): ?DocBlock\Description + { + return $this->description; + } + + public function setDescription(?DocBlock\Description $description) + { + $this->description = $description; + } + + public function getTags() + { + return $this->tags; + } + + public function appendTagline(string $tagline) + { + $tag = $this->getTagFactory()->create($tagline, $this->context); + + $this->appendTag($tag); + } + + public function hasTag(string $name): bool + { + foreach ($this->tags as $tag) { + if ($tag->getName() === $name) { + return true; + } + } + + return false; + } + + public function appendTag(Tag $tag) + { + $this->tags[] = $tag; + } + + public function removeTag(Tag $tagToRemove) + { + foreach ($this->tags as $i => $tag) { + if ($tag === $tagToRemove) { + unset($this->tags[$i]); + break; + } + } + } + + public function clearTags() + { + $this->tags = []; + } + + private function getTagFactory(): TagFactory + { + if ($this->tagFactory === null) { + $fqsenResolver = new LocalFsqenResolver(); + $tagFactory = new StandardTagFactory($fqsenResolver); + $descriptionFactory = new DescriptionFactory($tagFactory); + + $tagFactory->addService($descriptionFactory); + $tagFactory->addService(new TypeResolver($fqsenResolver)); + + $this->tagFactory = $tagFactory; + } + return $this->tagFactory; + } +} diff --git a/src/TypeResolver/LocalFsqenResolver.php b/src/DocBlock/LocalFsqenResolver.php similarity index 85% rename from src/TypeResolver/LocalFsqenResolver.php rename to src/DocBlock/LocalFsqenResolver.php index 7f0211f..bf9aa6c 100644 --- a/src/TypeResolver/LocalFsqenResolver.php +++ b/src/DocBlock/LocalFsqenResolver.php @@ -1,6 +1,6 @@ phpdoc = new DocBlock($method); + $this->phpdoc = DocBlockBuilder::createFromReflector($method); $this->addLocationToPhpDoc(); @@ -56,7 +56,7 @@ class Macro extends Method $name = $parameter->isVariadic() ? '...' : ''; $name .= '$' . $parameter->getName(); - $this->phpdoc->appendTag(Tag::createInstance("@param {$type} {$name}")); + $this->phpdoc->appendTagline("@param {$type} {$name}"); } } @@ -73,7 +73,7 @@ class Macro extends Method $type .= $return->allowsNull() ? '|null' : ''; } - $this->phpdoc->appendTag(Tag::createInstance("@return {$type}")); + $this->phpdoc->appendTagLine("@return {$type}"); } } @@ -109,9 +109,9 @@ class Macro extends Method }); if ($enclosingMethod) { - $this->phpdoc->appendTag(Tag::createInstance( + $this->phpdoc->appendTagLine( '@see \\' . $enclosingClass->getName() . '::' . $enclosingMethod->getName() . '()' - )); + ); } } diff --git a/src/Method.php b/src/Method.php index a46e3f2..78c8b00 100644 --- a/src/Method.php +++ b/src/Method.php @@ -11,18 +11,19 @@ namespace Barryvdh\LaravelIdeHelper; +use Barryvdh\LaravelIdeHelper\DocBlock\DocBlockBuilder; use Barryvdh\Reflection\DocBlock; use Barryvdh\Reflection\DocBlock\Context; -use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer; use Barryvdh\Reflection\DocBlock\Tag; use Barryvdh\Reflection\DocBlock\Tag\ParamTag; use Barryvdh\Reflection\DocBlock\Tag\ReturnTag; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Str; +use phpDocumentor\Reflection\DocBlock\Serializer; class Method { - /** @var DocBlock */ + /** @var DocBlockBuilder */ protected $phpdoc; /** @var \ReflectionMethod */ @@ -65,9 +66,9 @@ class Method //Normalize the description and inherit the docs from parents/interfaces try { - $this->normalizeParams($this->phpdoc); - $this->normalizeReturn($this->phpdoc); - $this->normalizeDescription($this->phpdoc); + // $this->normalizeParams($this->phpdoc); + // $this->normalizeReturn($this->phpdoc); + // $this->normalizeDescription($this->phpdoc); } catch (\Exception $e) { } @@ -75,7 +76,7 @@ class Method $this->getParameters($method); //Make the method static - $this->phpdoc->appendTag(Tag::createInstance('@static', $this->phpdoc)); + $this->phpdoc->appendTagline('@static'); } /** @@ -83,7 +84,8 @@ class Method */ protected function initPhpDoc($method) { - $this->phpdoc = new DocBlock($method, new Context($this->namespace, $this->classAliases)); + $context = new \phpDocumentor\Reflection\Types\Context($this->namespace, $this->classAliases); + $this->phpdoc = DocBlockBuilder::createFromReflector($method, $context); } /** @@ -145,8 +147,8 @@ class Method */ public function getDocComment($prefix = "\t\t") { - $serializer = new DocBlockSerializer(1, $prefix); - return $serializer->getDocComment($this->phpdoc); + $serializer = new Serializer(1, $prefix); + return $serializer->getDocComment($this->phpdoc->getDocBlock()); } /** @@ -191,39 +193,6 @@ class Method return $implode ? implode(', ', $this->params_with_default) : $this->params_with_default; } - /** - * Get the description and get the inherited docs. - * - * @param DocBlock $phpdoc - */ - protected function normalizeDescription(DocBlock $phpdoc) - { - //Get the short + long description from the DocBlock - $description = $phpdoc->getText(); - - //Loop through parents/interfaces, to fill in {@inheritdoc} - if (strpos($description, '{@inheritdoc}') !== false) { - $inheritdoc = $this->getInheritDoc($this->method); - $inheritDescription = $inheritdoc->getText(); - - $description = str_replace('{@inheritdoc}', $inheritDescription, $description); - $phpdoc->setText($description); - - $this->normalizeParams($inheritdoc); - $this->normalizeReturn($inheritdoc); - - //Add the tags that are inherited - $inheritTags = $inheritdoc->getTags(); - if ($inheritTags) { - /** @var Tag $tag */ - foreach ($inheritTags as $tag) { - $tag->setDocBlock(); - $phpdoc->appendTag($tag); - } - } - } - } - /** * Normalize the parameters * diff --git a/tests/MacroTest.php b/tests/MacroTest.php index cf1f79f..44f6525 100644 --- a/tests/MacroTest.php +++ b/tests/MacroTest.php @@ -5,10 +5,10 @@ declare(strict_types=1); namespace Barryvdh\LaravelIdeHelper\Tests; use Barryvdh\LaravelIdeHelper\Macro; -use Barryvdh\Reflection\DocBlock; -use Barryvdh\Reflection\DocBlock\Tag; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Routing\UrlGenerator; +use phpDocumentor\Reflection\DocBlock; +use phpDocumentor\Reflection\DocBlock\Tag; use ReflectionClass; use ReflectionFunction; use ReflectionFunctionAbstract; @@ -62,7 +62,7 @@ class MacroTest extends TestCase ); $this->assertNotNull($phpdoc); - $this->assertEmpty($phpdoc->getText()); + $this->assertEmpty($phpdoc->getSummary()); $this->assertEquals('@param int|null $a', $this->tagsToString($phpdoc, 'param')); $this->assertEquals('@return int', $this->tagsToString($phpdoc, 'return')); $this->assertTrue($phpdoc->hasTag('see')); @@ -86,7 +86,7 @@ class MacroTest extends TestCase ); $this->assertNotNull($phpdoc); - $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertStringContainsString('Test docblock', $phpdoc->getSummary()); $this->assertEquals('@param int|null $a', $this->tagsToString($phpdoc, 'param')); $this->assertEquals('@return int', $this->tagsToString($phpdoc, 'return')); $this->assertTrue($phpdoc->hasTag('see')); @@ -110,7 +110,7 @@ class MacroTest extends TestCase ); $this->assertNotNull($phpdoc); - $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertStringContainsString('Test docblock', $phpdoc->getSummary()); $this->assertEquals('@param int|null $a', $this->tagsToString($phpdoc, 'param')); $this->assertFalse($phpdoc->hasTag('return')); $this->assertTrue($phpdoc->hasTag('see')); @@ -134,7 +134,7 @@ class MacroTest extends TestCase ); $this->assertNotNull($phpdoc); - $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertStringContainsString('Test docblock', $phpdoc->getSummary()); $this->assertFalse($phpdoc->hasTag('param')); $this->assertEquals('@return int', $this->tagsToString($phpdoc, 'return')); $this->assertTrue($phpdoc->hasTag('see')); @@ -159,7 +159,7 @@ class MacroTest extends TestCase ); $this->assertNotNull($phpdoc); - $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertStringContainsString('Test docblock', $phpdoc->getSummary()); $this->assertEquals('@param \stdClass|null $a aaaaa', $this->tagsToString($phpdoc, 'param')); $this->assertEquals('@return int', $this->tagsToString($phpdoc, 'return')); } @@ -183,7 +183,7 @@ class MacroTest extends TestCase ); $this->assertNotNull($phpdoc); - $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertStringContainsString('Test docblock', $phpdoc->getSummary()); $this->assertEquals('@param mixed $a', $this->tagsToString($phpdoc, 'param')); $this->assertEquals('@return \stdClass|null rrrrrrr', $this->tagsToString($phpdoc, 'return')); } @@ -202,7 +202,7 @@ class MacroTest extends TestCase PHP)); $this->assertNotNull($phpdoc); - $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertStringContainsString('Test docblock', $phpdoc->getSummary()); $this->assertEquals('@param \Stringable|string|null $a', $this->tagsToString($phpdoc, 'param')); $this->assertEquals('@return \Stringable|string|null', $this->tagsToString($phpdoc, 'return')); } @@ -212,7 +212,7 @@ class MacroTest extends TestCase $tags = $docBlock->getTagsByName($name); $tags = array_map( function (Tag $tag) { - return trim((string)$tag); + return trim($tag->render()); }, $tags ); @@ -242,13 +242,13 @@ class MacroTest extends TestCase $macro = new Macro($reflectionMethod, 'URL', new ReflectionClass(UrlGenerator::class), 'macroName'); $output = <<<'DOC' /** - * + * * * @param string $foo * @param int $bar - * @return string + * @return string * @see \Barryvdh\LaravelIdeHelper\Tests\UrlGeneratorMacroClass::__invoke() - * @static + * @static */ DOC; $this->assertSame($output, $macro->getDocComment('')); @@ -276,7 +276,7 @@ class MacroMock extends Macro public function getPhpDoc(ReflectionFunctionAbstract $method, ReflectionClass $class = null): DocBlock { - return (new Macro($method, '', $class ?? $method->getClosureScopeClass()))->phpdoc; + return (new Macro($method, '', $class ?? $method->getClosureScopeClass()))->phpdoc->getDocBlock(); } }