Refactored method tag and added test

This commit is contained in:
Mike van Riel
2015-06-23 08:28:00 +02:00
parent 3b060b555e
commit de7bc574ea
2 changed files with 442 additions and 111 deletions
+112 -111
View File
@@ -1,62 +1,77 @@
<?php <?php
/** /**
* phpDocumentor * This file is part of phpDocumentor.
* *
* PHP Version 5.3 * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* *
* @author Mike van Riel <mike[email protected]> * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
* @license http://www.opensource.org/licenses/mit-license.php MIT * @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org * @link http://phpdoc.org
*/ */
namespace phpDocumentor\Reflection\DocBlock\Tags; namespace phpDocumentor\Reflection\DocBlock\Tags;
use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\Types\Void;
use Webmozart\Assert\Assert;
/** /**
* Reflection class for a @method in a Docblock. * Reflection class for an {@}method in a Docblock.
*
* @author Mike van Riel <[email protected]>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/ */
class Method extends Return_ final class Method extends BaseTag
{ {
protected $name = 'method';
/** @var string */ /** @var string */
protected $method_name = ''; private $methodName = '';
/** @var string */ /** @var string[] */
protected $arguments = ''; private $arguments = [];
/** @var bool */ /** @var bool */
protected $isStatic = false; private $isStatic = false;
/** /** @var Type */
* {@inheritdoc} private $returnType;
*/
public function getContent() public function __construct(
{ $methodName,
if (null === $this->description) { array $arguments = [],
$this->description = ''; Type $returnType = null,
if ($this->isStatic) { $static = false,
$this->description .= 'static '; Description $description = null
} ) {
$this->description .= $this->type . Assert::stringNotEmpty($methodName);
" {$this->method_name}({$this->arguments}) " . Assert::boolean($static);
$this->description;
if ($returnType === null) {
$returnType = new Void();
} }
return $this->description; $this->methodName = $methodName;
$this->arguments = $this->filterArguments($arguments);
$this->returnType = $returnType;
$this->isStatic = $static;
$this->description = $description;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setContent($content) public static function create(
{ $body,
Tag::setContent($content); TypeResolver $typeResolver = null,
DescriptionFactory $descriptionFactory = null,
Context $context = null
) {
Assert::stringNotEmpty($body);
Assert::allNotNull([ $typeResolver, $descriptionFactory ]);
// 1. none or more whitespace // 1. none or more whitespace
// 2. optionally the keyword "static" followed by whitespace // 2. optionally the keyword "static" followed by whitespace
// 3. optionally a word with underscores followed by whitespace : as // 3. optionally a word with underscores followed by whitespace : as
@@ -66,10 +81,10 @@ class Method extends Return_
// 5. then a word with underscores, followed by ( and any character // 5. then a word with underscores, followed by ( and any character
// until a ) and whitespace : as method name with signature // until a ) and whitespace : as method name with signature
// 6. any remaining text : as description // 6. any remaining text : as description
if (preg_match( if (!preg_match(
'/^ '/^
# Static keyword # Static keyword
# Declates a static method ONLY if type is also present # Declares a static method ONLY if type is also present
(?: (?:
(static) (static)
\s+ \s+
@@ -91,47 +106,36 @@ class Method extends Return_
# Description # Description
(.*) (.*)
$/sux', $/sux',
$this->description, $body,
$matches $matches
)) { )) {
list( return null;
,
$static,
$this->type,
$this->method_name,
$this->arguments,
$this->description
) = $matches;
if ($static) {
if (!$this->type) {
$this->type = 'static';
} else {
$this->isStatic = true;
}
} else {
if (!$this->type) {
$this->type = 'void';
}
}
$this->parsedDescription = null;
} }
return $this; list(, $static, $returnType, $methodName, $arguments, $description) = $matches;
}
/** $static = $static === 'static';
* Sets the name of this method. $returnType = $typeResolver->resolve($returnType, $context);
* $description = $descriptionFactory->create($description, $context);
* @param string $method_name The name of the method.
*
* @return $this
*/
public function setMethodName($method_name)
{
$this->method_name = $method_name;
$this->description = null; $arguments = explode(',', $arguments);
return $this; foreach($arguments as &$argument) {
$argument = explode(' ', trim($argument));
if ($argument[0][0] === '$') {
$argumentName = substr($argument[0], 1);
$argumentType = new Void();
} else {
$argumentType = $typeResolver->resolve($argument[0], $context);
$argumentName = '';
if (isset($argument[1])) {
$argumentName = substr($argument[1], 1);
}
}
$argument = [ 'name' => $argumentName, 'type' => $argumentType];
}
return new static($methodName, $arguments, $returnType, $static, $description);
} }
/** /**
@@ -141,51 +145,21 @@ class Method extends Return_
*/ */
public function getMethodName() public function getMethodName()
{ {
return $this->method_name; return $this->methodName;
} }
/** /**
* Sets the arguments for this method.
*
* @param string $arguments A comma-separated arguments line.
*
* @return void
*/
public function setArguments($arguments)
{
$this->arguments = $arguments;
$this->description = null;
return $this;
}
/**
* Returns an array containing each argument as array of type and name.
*
* Please note that the argument sub-array may only contain 1 element if no
* type was specified.
*
* @return string[] * @return string[]
*/ */
public function getArguments() public function getArguments()
{ {
if (empty($this->arguments)) { return $this->arguments;
return array();
}
$arguments = explode(',', $this->arguments);
foreach ($arguments as $key => $value) {
$arguments[$key] = explode(' ', trim($value));
}
return $arguments;
} }
/** /**
* Checks whether the method tag describes a static method or not. * Checks whether the method tag describes a static method or not.
* *
* @return bool TRUE if the method declaration is for a static method, FALSE * @return bool TRUE if the method declaration is for a static method, FALSE otherwise.
* otherwise.
*/ */
public function isStatic() public function isStatic()
{ {
@@ -193,17 +167,44 @@ class Method extends Return_
} }
/** /**
* Sets a new value for whether the method is static or not. * @return Type
*
* @param bool $isStatic The new value to set.
*
* @return $this
*/ */
public function setIsStatic($isStatic) public function getReturnType()
{ {
$this->isStatic = $isStatic; return $this->returnType;
}
$this->description = null; public function __toString()
return $this; {
$arguments = [];
foreach ($this->arguments as $argument) {
$arguments[] = $argument['type'] . ' $' . $argument['name'];
}
return ($this->isStatic() ? 'static ' : '')
. (string)$this->returnType . ' '
. $this->methodName
. '(' . implode(', ', $arguments) . ')'
. ($this->description ? ' ' . $this->description->render() : '');
}
private function filterArguments($arguments)
{
foreach ($arguments as &$argument) {
if (is_string($argument)) {
$argument = [ 'name' => $argument ];
}
if (! isset($argument['type'])) {
$argument['type'] = new Void();
}
$keys = array_keys($argument);
if ($keys !== [ 'name', 'type' ]) {
throw new \InvalidArgumentException(
'Arguments can only have the "name" and "type" fields, found: ' . var_export($keys, true)
);
}
}
return $arguments;
} }
} }
+330
View File
@@ -0,0 +1,330 @@
<?php
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright 2010-2015 Mike van Riel<[email protected]>
* @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\Fqsen;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\Types\Object_;
use phpDocumentor\Reflection\Types\String_;
use phpDocumentor\Reflection\Types\Void;
/**
* @coversDefaultClass \phpDocumentor\Reflection\DocBlock\Tags\Method
* @covers ::<private>
*/
class MethodTest extends \PHPUnit_Framework_TestCase
{
/**
* @uses \phpDocumentor\Reflection\DocBlock\Tags\Method::__construct
* @covers \phpDocumentor\Reflection\DocBlock\Tags\BaseTag::getName
*/
public function testIfCorrectTagNameIsReturned()
{
$fixture = new Method('myMethod');
$this->assertSame('method', $fixture->getName());
}
/**
* @uses \phpDocumentor\Reflection\DocBlock\Tags\Method::__construct
* @uses \phpDocumentor\Reflection\DocBlock\Tags\Method::isStatic
* @uses \phpDocumentor\Reflection\DocBlock\Tags\Method::__toString
* @uses \phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter
* @uses \phpDocumentor\Reflection\DocBlock\Description
* @covers \phpDocumentor\Reflection\DocBlock\Tags\BaseTag::render
*/
public function testIfTagCanBeRenderedUsingDefaultFormatter()
{
$arguments = [
['name' => 'argument1', 'type' => new String_()],
['name' => 'argument2', 'type' => new Object_()]
];
$fixture = new Method('myMethod', $arguments, new Void(), true, new Description('My Description'));
$this->assertSame(
'static void myMethod(string $argument1, object $argument2) My Description',
$fixture->render()
);
}
/**
* @uses \phpDocumentor\Reflection\DocBlock\Tags\Method::__construct
* @uses \phpDocumentor\Reflection\DocBlock\Description
* @covers \phpDocumentor\Reflection\DocBlock\Tags\BaseTag::render
*/
public function testIfTagCanBeRenderedUsingSpecificFormatter()
{
$fixture = new Method('myMethod');
$formatter = m::mock(Formatter::class);
$formatter->shouldReceive('format')->with($fixture)->andReturn('Rendered output');
$this->assertSame('Rendered output', $fixture->render($formatter));
}
/**
* @covers ::__construct
* @covers ::getMethodName
*/
public function testHasMethodName()
{
$expected = 'myMethod';
$fixture = new Method($expected);
$this->assertSame($expected, $fixture->getMethodName());
}
/**
* @covers ::__construct
* @covers ::getArguments
*/
public function testHasArguments()
{
$arguments = [
[ 'name' => 'argument1', 'type' => new String_() ]
];
$fixture = new Method('myMethod', $arguments);
$this->assertSame($arguments, $fixture->getArguments());
}
/**
* @covers ::__construct
* @covers ::getArguments
*/
public function testArgumentsMayBePassedAsString()
{
$arguments = ['argument1'];
$expected = [
[ 'name' => $arguments[0], 'type' => new Void() ]
];
$fixture = new Method('myMethod', $arguments);
$this->assertEquals($expected, $fixture->getArguments());
}
/**
* @covers ::__construct
* @covers ::getArguments
*/
public function testArgumentTypeCanBeInferredAsVoid()
{
$arguments = [ [ 'name' => 'argument1' ] ];
$expected = [
[ 'name' => $arguments[0]['name'], 'type' => new Void() ]
];
$fixture = new Method('myMethod', $arguments);
$this->assertEquals($expected, $fixture->getArguments());
}
/**
* @covers ::__construct
* @covers ::getReturnType
*/
public function testHasReturnType()
{
$expected = new String_();
$fixture = new Method('myMethod', [], $expected);
$this->assertSame($expected, $fixture->getReturnType());
}
/**
* @covers ::__construct
* @covers ::getReturnType
*/
public function testReturnTypeCanBeInferredAsVoid()
{
$fixture = new Method('myMethod', []);
$this->assertEquals(new Void(), $fixture->getReturnType());
}
/**
* @covers ::__construct
* @covers ::isStatic
*/
public function testMethodCanBeStatic()
{
$expected = false;
$fixture = new Method('myMethod', [], null, $expected);
$this->assertSame($expected, $fixture->isStatic());
$expected = true;
$fixture = new Method('myMethod', [], null, $expected);
$this->assertSame($expected, $fixture->isStatic());
}
/**
* @covers ::__construct
* @covers \phpDocumentor\Reflection\DocBlock\Tags\BaseTag::getDescription
* @uses \phpDocumentor\Reflection\DocBlock\Description
*/
public function testHasDescription()
{
$expected = new Description('Description');
$fixture = new Method('myMethod', [], null, false, $expected);
$this->assertSame($expected, $fixture->getDescription());
}
/**
* @covers ::__construct
* @covers ::__toString
* @uses \phpDocumentor\Reflection\DocBlock\Description
* @uses \phpDocumentor\Reflection\DocBlock\Tags\Method::isStatic
*/
public function testStringRepresentationIsReturned()
{
$arguments = [
['name' => 'argument1', 'type' => new String_()],
['name' => 'argument2', 'type' => new Object_()]
];
$fixture = new Method('myMethod', $arguments, new Void(), true, new Description('My Description'));
$this->assertSame(
'static void myMethod(string $argument1, object $argument2) My Description',
(string)$fixture
);
}
/**
* @covers ::create
* @uses \phpDocumentor\Reflection\DocBlock\Tags\Method::<public>
* @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory
* @uses \phpDocumentor\Reflection\TypeResolver
* @uses \phpDocumentor\Reflection\DocBlock\Description
* @uses \phpDocumentor\Reflection\Fqsen
* @uses \phpDocumentor\Reflection\Types\Context
*/
public function testFactoryMethod()
{
$descriptionFactory = m::mock(DescriptionFactory::class);
$resolver = new TypeResolver();
$context = new Context('');
$description = new Description('My Description');
$expectedArguments = [
[ 'name' => 'argument1', 'type' => new String_() ],
[ 'name' => 'argument2', 'type' => new Void() ]
];
$descriptionFactory->shouldReceive('create')->with('My Description', $context)->andReturn($description);
$fixture = Method::create(
'static void myMethod(string $argument1, $argument2) My Description',
$resolver,
$descriptionFactory,
$context
);
$this->assertSame('static void myMethod(string $argument1, void $argument2) My Description', (string)$fixture);
$this->assertSame('myMethod', $fixture->getMethodName());
$this->assertEquals($expectedArguments, $fixture->getArguments());
$this->assertInstanceOf(Void::class, $fixture->getReturnType());
$this->assertSame($description, $fixture->getDescription());
}
/**
* @covers ::create
* @expectedException \InvalidArgumentException
*/
public function testFactoryMethodFailsIfBodyIsNotString()
{
Method::create([]);
}
/**
* @covers ::create
* @expectedException \InvalidArgumentException
*/
public function testFactoryMethodFailsIfBodyIsEmpty()
{
Method::create('');
}
/**
* @covers ::create
* @expectedException \InvalidArgumentException
*/
public function testFactoryMethodReturnsNullIfBodyIsIncorrect()
{
$this->assertNull(Method::create('body('));
}
/**
* @covers ::create
* @expectedException \InvalidArgumentException
*/
public function testFactoryMethodFailsIfResolverIsNull()
{
Method::create('body');
}
/**
* @covers ::create
* @expectedException \InvalidArgumentException
*/
public function testFactoryMethodFailsIfDescriptionFactoryIsNull()
{
Method::create('body', new TypeResolver());
}
/**
* @covers ::__construct
* @expectedException \InvalidArgumentException
*/
public function testCreationFailsIfBodyIsNotString()
{
new Method([]);
}
/**
* @covers ::__construct
* @expectedException \InvalidArgumentException
*/
public function testCreationFailsIfBodyIsEmpty()
{
new Method('');
}
/**
* @covers ::__construct
* @expectedException \InvalidArgumentException
*/
public function testCreationFailsIfStaticIsNotBoolean()
{
new Method('body', [], null, []);
}
/**
* @covers ::__construct
* @expectedException \InvalidArgumentException
*/
public function testCreationFailsIfArgumentRecordContainsInvalidEntry()
{
new Method('body', [ [ 'name' => 'myName', 'unknown' => 'nah' ] ]);
}
}