diff --git a/src/Macro.php b/src/Macro.php index 0a4b630..069770d 100644 --- a/src/Macro.php +++ b/src/Macro.php @@ -33,23 +33,25 @@ class Macro extends Method */ protected function initPhpDoc($method) { - $this->phpdoc = new DocBlock('/** */'); + $this->phpdoc = new DocBlock($method); $this->addLocationToPhpDoc(); - // Add macro parameters - foreach ($method->getParameters() as $parameter) { - $type = $parameter->hasType() ? $parameter->getType()->getName() : 'mixed'; - $type .= $parameter->hasType() && $parameter->getType()->allowsNull() ? '|null' : ''; + // Add macro parameters if they are missed in original docblock + if (!$this->phpdoc->hasTag('param')) { + foreach ($method->getParameters() as $parameter) { + $type = $parameter->hasType() ? $parameter->getType()->getName() : 'mixed'; + $type .= $parameter->hasType() && $parameter->getType()->allowsNull() ? '|null' : ''; - $name = $parameter->isVariadic() ? '...' : ''; - $name .= '$' . $parameter->getName(); + $name = $parameter->isVariadic() ? '...' : ''; + $name .= '$' . $parameter->getName(); - $this->phpdoc->appendTag(Tag::createInstance("@param {$type} {$name}")); + $this->phpdoc->appendTag(Tag::createInstance("@param {$type} {$name}")); + } } - // Add macro return type - if ($method->hasReturnType()) { + // Add macro return type if it missed in original docblock + if ($method->hasReturnType() && !$this->phpdoc->hasTag('return')) { $builder = EloquentBuilder::class; $return = $method->getReturnType(); diff --git a/tests/MacroTest.php b/tests/MacroTest.php index 6c9a486..ea9dd0b 100644 --- a/tests/MacroTest.php +++ b/tests/MacroTest.php @@ -46,6 +46,147 @@ class MacroTest extends TestCase ); } + /** + * @covers ::initPhpDoc + * @throws \ReflectionException + */ + public function testInitPhpDocClosureWithoutDocBlock(): void + { + $phpdoc = (new MacroMock())->getPhpDoc( + new ReflectionFunction( + function (int $a = null): int { + return 0; + } + ) + ); + + $this->assertNotNull($phpdoc); + $this->assertEmpty($phpdoc->getText()); + $this->assertEquals('@param int|null $a', $this->tagsToString($phpdoc, 'param')); + $this->assertEquals('@return int', $this->tagsToString($phpdoc, 'return')); + $this->assertTrue($phpdoc->hasTag('see')); + } + + /** + * @covers ::initPhpDoc + * @throws \ReflectionException + */ + public function testInitPhpDocClosureWithArgsAndReturnType(): void + { + $phpdoc = (new MacroMock())->getPhpDoc( + new ReflectionFunction( + /** + * Test docblock. + */ + function (int $a = null): int { + return 0; + } + ) + ); + + $this->assertNotNull($phpdoc); + $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertEquals('@param int|null $a', $this->tagsToString($phpdoc, 'param')); + $this->assertEquals('@return int', $this->tagsToString($phpdoc, 'return')); + $this->assertTrue($phpdoc->hasTag('see')); + } + + /** + * @covers ::initPhpDoc + * @throws \ReflectionException + */ + public function testInitPhpDocClosureWithArgs(): void + { + $phpdoc = (new MacroMock())->getPhpDoc( + new ReflectionFunction( + /** + * Test docblock. + */ + function (int $a = null) { + return 0; + } + ) + ); + + $this->assertNotNull($phpdoc); + $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertEquals('@param int|null $a', $this->tagsToString($phpdoc, 'param')); + $this->assertFalse($phpdoc->hasTag('return')); + $this->assertTrue($phpdoc->hasTag('see')); + } + + /** + * @covers ::initPhpDoc + * @throws \ReflectionException + */ + public function testInitPhpDocClosureWithReturnType(): void + { + $phpdoc = (new MacroMock())->getPhpDoc( + new ReflectionFunction( + /** + * Test docblock. + */ + function (): int { + return 0; + } + ) + ); + + $this->assertNotNull($phpdoc); + $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertFalse($phpdoc->hasTag('param')); + $this->assertEquals('@return int', $this->tagsToString($phpdoc, 'return')); + $this->assertTrue($phpdoc->hasTag('see')); + } + + /** + * @covers ::initPhpDoc + */ + public function testInitPhpDocParamsAddedOnlyNotPresent(): void + { + $phpdoc = (new MacroMock())->getPhpDoc( + new ReflectionFunction( + /** + * Test docblock. + * + * @param \stdClass|null $a aaaaa + */ + function ($a = null): int { + return 0; + } + ) + ); + + $this->assertNotNull($phpdoc); + $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertEquals('@param \stdClass|null $a aaaaa', $this->tagsToString($phpdoc, 'param')); + $this->assertEquals('@return int', $this->tagsToString($phpdoc, 'return')); + } + + /** + * @covers ::initPhpDoc + */ + public function testInitPhpDocReturnAddedOnlyNotPresent(): void + { + $phpdoc = (new MacroMock())->getPhpDoc( + new ReflectionFunction( + /** + * Test docblock. + * + * @return \stdClass|null rrrrrrr + */ + function ($a = null): int { + return 0; + } + ) + ); + + $this->assertNotNull($phpdoc); + $this->assertStringContainsString('Test docblock', $phpdoc->getText()); + $this->assertEquals('@param mixed $a', $this->tagsToString($phpdoc, 'param')); + $this->assertEquals('@return \stdClass|null rrrrrrr', $this->tagsToString($phpdoc, 'return')); + } + protected function tagsToString(DocBlock $docBlock, string $name) { $tags = $docBlock->getTagsByName($name);