Compare commits

...
10 Commits
Author SHA1 Message Date
Mike van Riel 2b05ea3221 Fixing the other RegExes as well since they were also not using the u modifier 2012-07-13 20:07:51 +02:00
Mike van Riel 33a2e55cbc Merge pull request #4 from rvanvelzen/master
Add the u modifier to the regexes used.
2012-07-13 10:28:21 -07:00
Richard van Velzen 3d97a72bac Merge branch 'master' of git://github.com/phpDocumentor/ReflectionDocBlock 2012-07-13 13:43:29 +02:00
Richard van Velzen 51210b581e Add the u modifier to the regexes used.
Without the u modifier, PCRE does not recognize Unicode sequences, which breaks certain cases.
2012-07-13 13:42:49 +02:00
Mike van Riel cee3c0bc21 Namespace resolution did not work as intended
The algorithm to expand a class name into a FCQN contained seevral errors.
These are now gone and expansion should work as expected
2012-06-30 13:38:18 +02:00
Mike van Riel 731e797dd8 Added 'global' to the list of special namespace names indicating global space and adding namespace resolution to the param tags 'getType()' method 2012-06-29 23:04:12 +02:00
Mike van Riel 838fbfa43f Added command to set the DocBlock onto the tags so that type expansion may happen 2012-06-21 23:03:48 +02:00
Mike van Riel 4d262053fc Changed return tag to properly support expanding types 2012-06-21 23:00:13 +02:00
Mike van Riel bbac3fa418 Added type expansion for the @param tag 2012-06-21 22:51:29 +02:00
Mike van Riel cd252c1f09 Added support for namespace expansion of a DocBlock and via the DocBlock to its tags 2012-06-21 22:17:30 +02:00
8 changed files with 407 additions and 36 deletions
+112 -7
View File
@@ -36,14 +36,36 @@ class DocBlock implements \Reflector
*/
protected $tags = array();
/** @var string the current namespace */
protected $namespace = '\\';
/** @var string[] List of namespace aliases => Fully Qualified Namespace */
protected $namespace_aliases = array();
/**
* Parses the given docblock and populates the member fields.
*
* @param string|\Reflector $docblock A docblock comment (including asterisks)
* The constructor may also receive namespace information such as the
* current namespace and aliases. This information is used in the
* {@link expandType()} method to transform a relative Type into a FQCN.
*
* For example the param and return tags use this to expand their type
* information.
*
* @param \Reflector|string $docblock A docblock comment (including asterisks)
* or reflector supporting the getDocComment method.
* @param string $namespace The namespace where this DocBlock resides in;
* defaults to `\`.
* @param string[] $namespace_aliases a list of namespace aliases as
* provided by the `use` keyword; the key of the array is the alias name
* or last part of the alias array if no alias name is provided.
*
* @throws \InvalidArgumentException if the given argument does not have the
* getDocComment method.
*/
public function __construct($docblock)
{
public function __construct(
$docblock, $namespace = '\\', $namespace_aliases = array()
) {
if (is_object($docblock)) {
if (!method_exists($docblock, 'getDocComment')) {
throw new \InvalidArgumentException(
@@ -61,6 +83,9 @@ class DocBlock implements \Reflector
$this->short_description = $short;
$this->long_description = new DocBlock\LongDescription($long);
$this->parseTags($tags);
$this->namespace = $namespace;
$this->namespace_aliases = $namespace_aliases;
}
/**
@@ -74,7 +99,7 @@ class DocBlock implements \Reflector
{
$comment = trim(
preg_replace(
'#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#', '$1', $comment
'#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', '$1', $comment
)
);
@@ -108,7 +133,7 @@ class DocBlock implements \Reflector
} else {
// clears all extra horizontal whitespace from the line endings
// to prevent parsing issues
$comment = preg_replace('~(?m)\h*$~', '', $comment);
$comment = preg_replace('~(?m)\h*$~u', '', $comment);
/*
* Splits the docblock into a short description, long description and
@@ -148,7 +173,7 @@ class DocBlock implements \Reflector
)
)?
(\s+ [\s\S]*)? # everything that follows
/', $comment, $matches
/u', $comment, $matches
);
array_shift($matches);
}
@@ -192,7 +217,9 @@ class DocBlock implements \Reflector
// create proper Tag objects
foreach ($result as $key => $tag_line) {
$result[$key] = DocBlock\Tag::createInstance($tag_line);
$tag = DocBlock\Tag::createInstance($tag_line);
$tag->setDocBlock($this);
$result[$key] = $tag;
}
$this->tags = $result;
@@ -271,6 +298,84 @@ class DocBlock implements \Reflector
return false;
}
/**
* Tries to expand a type to it's full namespaced equivalent (FQCN).
*
* This method will take the given type and examine the current namespace
* and namespace aliases to see whether it should expand it into a FQCN
* as defined by the rules in PHP.
*
* @param string $type Type to expand into full namespaced
* equivalent.
* @param string[] $ignore_keywords Whether to ignore given keywords, when
* null it will use the default keywords: 'string', 'int', 'integer',
* 'bool', 'boolean', 'float', 'double', 'object', 'mixed', 'array',
* 'resource', 'void', 'null', 'callback', 'false', 'true'.
* Default value for this parameter is null.
*
* @return string
*/
public function expandType($type, $ignore_keywords = null)
{
if ($type === null) {
return null;
}
if ($ignore_keywords === null) {
$ignore_keywords = array(
'string', 'int', 'integer', 'bool', 'boolean', 'float', 'double',
'object', 'mixed', 'array', 'resource', 'void', 'null',
'callback', 'false', 'true'
);
}
$namespace = '';
if ($this->namespace != 'default' && $this->namespace != 'global') {
$namespace = rtrim($this->namespace, '\\') . '\\';
}
$type = explode('|', $type);
foreach ($type as &$item) {
$item = trim($item);
// add support for array notation
$is_array = false;
if (substr($item, -2) == '[]') {
$item = substr($item, 0, -2);
$is_array = true;
}
if ((substr($item, 0, 1) != '\\')
&& (!in_array(strtolower($item), $ignore_keywords))
) {
$type_parts = explode('\\', $item);
// if the first segment is an alias; replace with full name
if (isset($this->namespace_aliases[$type_parts[0]])) {
$type_parts[0] = $this->namespace_aliases[$type_parts[0]];
$item = implode('\\', $type_parts);
} else {
// otherwise prepend the current namespace
$item = $namespace . $item;
}
}
// full paths always start with a slash
if (isset($item[0]) && ($item[0] !== '\\')
&& (!in_array(strtolower($item), $ignore_keywords))
) {
$item = '\\' . $item;
}
// re-add the array notation markers
if ($is_array) {
$item .= '[]';
}
}
return implode('|', $type);
}
/**
* Builds a string representation of this object.
*
@@ -35,7 +35,7 @@ class LongDescription implements \Reflector
*/
public function __construct($content)
{
if (preg_match('/\{\@(.+?)\}/', $content, $matches)) {
if (preg_match('/\{\@(.+?)\}/u', $content, $matches)) {
array_shift($matches);
foreach ($matches as $tag) {
$this->tags[] = Tag::createInstance('@' . $tag);
@@ -33,7 +33,7 @@ class Tag implements \Reflector
/** @var int line number of the tag */
protected $line_number = 0;
/** @var object docblock class */
/** @var \phpDocumentor\Reflection\DocBlock docblock class */
protected $docblock;
/**
@@ -63,7 +63,12 @@ class ParamTag extends Tag
public function getTypes()
{
$types = explode('|', $this->type);
array_walk($types, 'trim');
foreach ($types as &$type) {
$type = !empty($type) && $this->docblock
? $this->docblock->expandType($type)
: trim($type);
}
return $types;
}
@@ -74,7 +79,7 @@ class ParamTag extends Tag
*/
public function getType()
{
return $this->type;
return $this->docblock->expandType($this->type);
}
/**
@@ -42,25 +42,4 @@ class ReturnTag extends ParamTag
$this->description = implode(' ', $content);
}
/**
* Returns the type of the variable.
*
* @return string
*/
public function getTypes()
{
$types = explode('|', $this->type);
array_walk($types, 'trim');
return $types;
}
/**
* Returns the type of the variable.
*
* @return string
*/
public function getType()
{
return $this->type;
}
}
@@ -0,0 +1,118 @@
<?php
/**
* phpDocumentor Param tag test.
*
* @author Mike van Riel <[email protected]>
* @copyright Copyright (c) 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com)
*/
namespace phpDocumentor\Reflection\DocBlock\Tag;
require_once __DIR__ . '/../../../../../src/phpDocumentor/Reflection/DocBlock/Tag/ParamTag.php';
/**
* Test class for phpDocumentor_Reflection_DocBlock_Param.
*
* @author Mike van Riel <[email protected]>
* @copyright Copyright (c) 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com)
*/
class ParamTagTest extends \PHPUnit_Framework_TestCase
{
/**
* Test that the \phpDocumentor\Reflection\DocBlock\Tag\ParamTag can
* understand the param DocBlock.
*
* @param string $type
* @param string $content
* @param string $extracted_type
* @param string $extracted_variable_name
* @param string $extracted_description
*
* @covers \phpDocumentor\Reflection\DocBlock\Tag\ParamTag::__construct
*
* @dataProvider provideDataForConstructor
*
* @return void
*/
public function testConstructorParsesInputsIntoCorrectFields(
$type, $content, $extracted_type, $extracted_variable_name,
$extracted_description
) {
$tag = new ParamTag($type, $content);
$this->assertEquals($extracted_type, $tag->getTypes());
$this->assertEquals($extracted_variable_name, $tag->getVariableName());
$this->assertEquals($extracted_description, $tag->getDescription());
}
/**
* Tests whether the getTypes method correctly converts the given tags.
*
* @param string $type Type string to test
* @param string[] $expected Array of expected types
*
* @covers \phpDocumentor\Reflection\DocBlock\Tag\ParamTag::getTypes()
*
* @dataProvider provideTypesToExpand
*/
public function testExpandTypeIntoCorrectFcqn($type, $expected)
{
$docblock = new \phpDocumentor\Reflection\DocBlock(
'', '\My\Namespace', array('Alias' => '\My\Namespace\Aliasing')
);
$tag = new ParamTag('param', $type.' $my_type');
$tag->setDocBlock($docblock);
$this->assertEquals($expected, $tag->getTypes());
}
/**
* Data provider for testConstructorParsesInputsIntoCorrectFields()
*
* @return array
*/
public function provideDataForConstructor()
{
return array(
array('param', 'int', array(''), '', 'int'),
array('param', '$bob', array(''), '$bob', ''),
array(
'param', 'int Number of bobs', array('int'), '',
'Number of bobs'
),
array('param', 'int $bob', array('int'), '$bob', ''),
array(
'param', 'int $bob Number of bobs', array('int'), '$bob',
'Number of bobs'
),
);
}
/**
* Returns the types and their expected values to test the retrieval of
* types.
*
* @return string[]
*/
public function provideTypesToExpand()
{
return array(
array('', array('')),
array(' ', array('')),
array('int', array('int')),
array('int ', array('int')),
array('string', array('string')),
array('DocBlock', array('\My\Namespace\DocBlock')),
// array(' DocBlock ', array('\My\Namespace\DocBlock')), FIXME
array('Alias\DocBlock', array('\My\Namespace\Aliasing\DocBlock')),
array(
'DocBlock|Tag',
array('\My\Namespace\DocBlock', '\My\Namespace\Tag')
),
array(
'DocBlock|null',
array('\My\Namespace\DocBlock', 'null')
),
);
}
}
@@ -0,0 +1,81 @@
<?php
/**
* phpDocumentor Return tag test.
*
* @author Mike van Riel <[email protected]>
* @copyright Copyright (c) 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com)
*/
namespace phpDocumentor\Reflection\DocBlock\Tag;
require_once __DIR__
. '/../../../../../src/phpDocumentor/Reflection/DocBlock/Tag/ReturnTag.php';
/**
* Test class for phpDocumentor_Reflection_DocBlock_ReturnTag.
*
* @author Mike van Riel <[email protected]>
* @copyright Copyright (c) 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com)
*/
class ReturnTagTest extends ParamTagTest
{
/**
* Test that the \phpDocumentor\Reflection\DocBlock\Tag\ReturnTag can
* understand the Return DocBlock.
*
* @param string $content
* @param string $extracted_type
* @param string $extracted_description
*
* @covers \phpDocumentor\Reflection\DocBlock\Tag\ReturnTag::__construct
*
* @dataProvider provideDataForConstructor
*
* @return void
*/
public function testConstructorParsesInputsIntoCorrectFields(
$content, $extracted_type, $extracted_description
) {
$tag = new ReturnTag('return', $content);
$this->assertEquals($extracted_type, $tag->getTypes());
$this->assertEquals($extracted_description, $tag->getDescription());
}
/**
* Tests whether the getTypes method correctly converts the given tags.
*
* @param string $type Type string to test
* @param string[] $expected Array of expected types
*
* @covers \phpDocumentor\Reflection\DocBlock\Tag\ReturnTag::getTypes()
*
* @dataProvider provideTypesToExpand
*
* @return void
*/
public function testExpandTypeIntoCorrectFcqn($type, $expected)
{
$docblock = new \phpDocumentor\Reflection\DocBlock(
'', '\My\Namespace', array('Alias' => '\My\Namespace\Aliasing')
);
$tag = new ReturnTag('return', $type);
$tag->setDocBlock($docblock);
$this->assertEquals($expected, $tag->getTypes());
}
/**
* Data provider for testConstructorParsesInputsIntoCorrectFields()
*
* @return array
*/
public function provideDataForConstructor()
{
return array(
array('', array(''), ''),
array('int', array('int'), ''),
array('int Number of Bobs', array('int'), 'Number of Bobs'),
);
}
}
@@ -11,7 +11,8 @@
namespace phpDocumentor\Reflection;
require_once __DIR__.'/../../../src/phpDocumentor/Reflection/DocBlock.php';
require_once __DIR__.'/../../../src/phpDocumentor/Reflection/DocBlock/LongDescription.php';
require_once __DIR__
.'/../../../src/phpDocumentor/Reflection/DocBlock/LongDescription.php';
/**
* Test class for phpDocumentor_Reflection_DocBlock
@@ -38,7 +39,8 @@ DOCBLOCK;
'This is a short description.', $object->getShortDescription()
);
$this->assertEquals(
'This is a long description.', $object->getLongDescription()->getContents()
'This is a long description.',
$object->getLongDescription()->getContents()
);
$this->assertEquals(2, count($object->getTags()));
$this->assertTrue($object->hasTag('see'));
@@ -58,8 +60,89 @@ DOCBLOCK;
'This is a short description.', $object->getShortDescription()
);
$this->assertEquals(
"This is a long description.\nThis is a continuation of the long description.", $object->getLongDescription()->getContents()
);
"This is a long description.\nThis is a continuation of the long "
."description.", $object->getLongDescription()->getContents()
);
}
/**
* Tests whether a type is expanded with the given namespace and that a
* keyword is not expanded.
*
* @covers \phpDocumentor\Reflection\DocBlock::expandType()
*
* @return void
*/
public function testExpandTypeUsingNamespace()
{
$docblock = new DocBlock('', '\My\Namespace');
$this->assertEquals('\My\Namespace\Mine', $docblock->expandType('Mine'));
}
/**
* Tests whether a type is expanded when no namespace is given.
*
* @covers \phpDocumentor\Reflection\DocBlock::expandType()
*
* @return void
*/
public function testExpandTypeWithoutNamespace()
{
$docblock = new DocBlock('');
$this->assertEquals('\Mine', $docblock->expandType('Mine'));
}
/**
* Tests whether a type is expanded with the given namespace when an alias
* is provided.
*
* @covers \phpDocumentor\Reflection\DocBlock::expandType()
*
* @return void
*/
public function testExpandTypeUsingNamespaceAlias()
{
$docblock = new DocBlock(
'', '\My\Namespace', array('Alias' => '\My\Namespace\Alias')
);
// first try a normal resolution without alias
$this->assertEquals(
'\My\Namespace\Al', $docblock->expandType('Al')
);
// try to use the alias
$this->assertEquals(
'\My\Namespace\Alias\Al', $docblock->expandType('Alias\Al')
);
}
/**
* Tests whether the keywords that should not be converted are not converted.
*
* @param string $keyword The keyword that is to be tested; this is provided
* by the dataprovider.
*
* @covers \phpDocumentor\Reflection\DocBlock::expandType()
*
* @dataProvider getNonExpandableKeywordsForExpandType
*
* @return void
*/
public function testThatExpandTypeDoesNotExpandAllKeywords($keyword)
{
$docblock = new DocBlock('', '\My\Namespace');
$this->assertEquals($keyword, $docblock->expandType($keyword));
}
public function getNonExpandableKeywordsForExpandType()
{
return array(
array('string'), array('int'), array('integer'), array('bool'),
array('boolean'), array('float'), array('double'), array('object'),
array('mixed'), array('array'), array('resource'), array('void'),
array('null'), array('callback'), array('false'), array('true')
);
}
}