From a7a4d3ff698d8bac031fb266422dd00986d9cc9d Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Sun, 21 Jun 2015 08:47:34 +0200 Subject: [PATCH] Rewritten Property tag and added test --- src/DocBlock/Tags/Property.php | 108 +++++++++++- tests/unit/DocBlock/Tags/PropertyTest.php | 200 ++++++++++++++++++++++ 2 files changed, 303 insertions(+), 5 deletions(-) create mode 100644 tests/unit/DocBlock/Tags/PropertyTest.php diff --git a/src/DocBlock/Tags/Property.php b/src/DocBlock/Tags/Property.php index 9846e16..12de67f 100644 --- a/src/DocBlock/Tags/Property.php +++ b/src/DocBlock/Tags/Property.php @@ -1,20 +1,118 @@ - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @copyright 2010-2015 Mike van Riel * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ namespace phpDocumentor\Reflection\DocBlock\Tags; +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\TypeResolver; +use phpDocumentor\Reflection\Types\Context; +use Webmozart\Assert\Assert; + /** - * Reflection class for a @property tag in a Docblock. + * Reflection class for a {@}property tag in a Docblock. */ class Property extends Param { + /** @var string */ + protected $name = 'property'; + + /** @var Type */ + private $type; + + /** @var string */ + protected $variableName = ''; + + /** + * @param string $variableName + * @param Type $type + * @param Description $description + */ + public function __construct($variableName, Type $type = null, Description $description = null) + { + Assert::string($variableName); + + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + TypeResolver $typeResolver = null, + DescriptionFactory $descriptionFactory = null, + Context $context = null + ) { + Assert::stringNotEmpty($body); + Assert::allNotNull([$typeResolver, $descriptionFactory]); + + $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); + $type = null; + $variableName = ''; + + // if the first item that is encountered is not a variable; it is a type + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { + $type = $typeResolver->resolve(array_shift($parts), $context); + array_shift($parts); + } + + // if the next item starts with a $ or ...$ it must be the variable name + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$')) { + $variableName = array_shift($parts); + array_shift($parts); + + if (substr($variableName, 0, 1) === '$') { + $variableName = substr($variableName, 1); + } + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $description); + } + + /** + * Returns the variable's name. + * + * @return string + */ + public function getVariableName() + { + return $this->variableName; + } + + /** + * Returns the variable's type or null if unknown. + * + * @return Type|null + */ + public function getType() + { + return $this->type; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return ($this->type ? $this->type . ' ' : '') + . '$' . $this->variableName + . ($this->description ? ' ' . $this->description : ''); + } } diff --git a/tests/unit/DocBlock/Tags/PropertyTest.php b/tests/unit/DocBlock/Tags/PropertyTest.php new file mode 100644 index 0000000..0a0be3d --- /dev/null +++ b/tests/unit/DocBlock/Tags/PropertyTest.php @@ -0,0 +1,200 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use Mockery as m; +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\TypeResolver; +use phpDocumentor\Reflection\Types\Context; +use phpDocumentor\Reflection\Types\String_; + +/** + * @coversDefaultClass \phpDocumentor\Reflection\DocBlock\Tags\Property + * @covers :: + */ +class PropertyTest extends \PHPUnit_Framework_TestCase +{ + /** + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Property::__construct + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @covers \phpDocumentor\Reflection\DocBlock\Tags\BaseTag::getName + */ + public function testIfCorrectTagNameIsReturned() + { + $fixture = new Property('myProperty', null, new Description('Description')); + + $this->assertSame('property', $fixture->getName()); + } + + /** + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Property::__construct + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Property::__toString + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @covers \phpDocumentor\Reflection\DocBlock\Tags\BaseTag::render + */ + public function testIfTagCanBeRenderedUsingDefaultFormatter() + { + $fixture = new Property('myProperty', new String_(), new Description('Description')); + $this->assertSame('string $myProperty Description', $fixture->render()); + + $fixture = new Property('myProperty', null, new Description('Description')); + $this->assertSame('$myProperty Description', $fixture->render()); + + $fixture = new Property('myProperty'); + $this->assertSame('$myProperty', $fixture->render()); + } + + /** + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Property::__construct + * @covers \phpDocumentor\Reflection\DocBlock\Tags\BaseTag::render + */ + public function testIfTagCanBeRenderedUsingSpecificFormatter() + { + $fixture = new Property('myProperty'); + + $formatter = m::mock(Formatter::class); + $formatter->shouldReceive('format')->with($fixture)->andReturn('Rendered output'); + + $this->assertSame('Rendered output', $fixture->render($formatter)); + } + + /** + * @covers ::__construct + * @covers ::getVariableName + */ + public function testHasVariableName() + { + $expected = 'myProperty'; + + $fixture = new Property($expected); + + $this->assertSame($expected, $fixture->getVariableName()); + } + + /** + * @covers ::__construct + * @covers ::getType + */ + public function testHasType() + { + $expected = new String_(); + + $fixture = new Property('myProperty', $expected); + + $this->assertSame($expected, $fixture->getType()); + } + + /** + * @covers ::__construct + * @covers \phpDocumentor\Reflection\DocBlock\Tags\BaseTag::getDescription + * @uses \phpDocumentor\Reflection\DocBlock\Description + */ + public function testHasDescription() + { + $expected = new Description('Description'); + + $fixture = new Property('1.0', null, $expected); + + $this->assertSame($expected, $fixture->getDescription()); + } + + /** + * @covers ::__construct + * @covers ::isVariadic + * @covers ::__toString + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @uses \phpDocumentor\Reflection\Types\String_ + */ + public function testStringRepresentationIsReturned() + { + $fixture = new Property('myProperty', new String_(), new Description('Description')); + + $this->assertSame('string $myProperty Description', (string)$fixture); + } + + /** + * @covers ::create + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Property:: + * @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @uses \phpDocumentor\Reflection\Types\Context + */ + public function testFactoryMethod() + { + $typeResolver = new TypeResolver(); + $descriptionFactory = m::mock(DescriptionFactory::class); + $context = new Context(''); + + $description = new Description('My Description'); + $descriptionFactory->shouldReceive('create')->with('My Description', $context)->andReturn($description); + + $fixture = Property::create('string $myProperty My Description', $typeResolver, $descriptionFactory, $context); + + $this->assertSame('string $myProperty My Description', (string)$fixture); + $this->assertSame('myProperty', $fixture->getVariableName()); + $this->assertInstanceOf(String_::class, $fixture->getType()); + $this->assertSame($description, $fixture->getDescription()); + } + + /** + * @covers ::create + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Property:: + * @uses \phpDocumentor\Reflection\TypeResolver + * @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory + * @expectedException \InvalidArgumentException + */ + public function testFactoryMethodFailsIfEmptyBodyIsGiven() + { + $descriptionFactory = m::mock(DescriptionFactory::class); + Property::create('', new TypeResolver(), $descriptionFactory); + } + + /** + * @covers ::create + * @expectedException \InvalidArgumentException + */ + public function testFactoryMethodFailsIfBodyIsNotString() + { + Property::create([]); + } + + /** + * @covers ::create + * @expectedException \InvalidArgumentException + */ + public function testFactoryMethodFailsIfResolverIsNull() + { + Property::create('body'); + } + + /** + * @covers ::create + * @uses \phpDocumentor\Reflection\TypeResolver + * @expectedException \InvalidArgumentException + */ + public function testFactoryMethodFailsIfDescriptionFactoryIsNull() + { + Property::create('body', new TypeResolver()); + } + + /** + * @covers ::__construct + * @expectedException \InvalidArgumentException + */ + public function testExceptionIsThrownIfVariableNameIsNotString() + { + new Property([]); + } +}