Compare commits

..
22 Commits
Author SHA1 Message Date
Mike van Riel c999e7d32a Removed the prefixing slash in a class_exists call because the autoloader of Composer
dies on that occasionally.
2012-07-26 17:09:50 +02:00
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
Mike van Riel 8f8cbd9ab6 Merge pull request #3 from rvanvelzen/master
Fix phpDocumentor/phpDocumentor2#467
2012-06-04 13:34:54 -07:00
Richard van Velzen a5b50ba482 Fixed the case where the dot does not properly terminate the short description.
The regular expression only took into account newlines, not general whitespace after the dot. Added a test case as well.
2012-06-04 09:27:56 +02:00
Mike van Riel f0a2901e56 Updated README to reflect submission to packagist 2012-06-02 22:07:17 +02:00
Mike van Riel bc982b7d51 Merge pull request #2 from egeniq/master
Small path fix
2012-06-02 13:00:11 -07:00
= b892415ca3 Fix for relative paths 2012-06-02 21:44:44 +02:00
Mike van Riel 37a1ac2e19 Merge branch 'master' of github.com:phpDocumentor/ReflectionDocBlock 2012-05-26 10:29:51 +02:00
Mike van Riel 663df2c65b Expanded README file and added Markdown class as dependency since it is used in the long description and parameter parsing 2012-05-26 10:28:28 +02:00
Mike van Riel 0b096ce2e7 Merge pull request #1 from leofeyer/master
Broken markdown support for long descriptions
2012-05-14 23:26:52 -07:00
Leo Feyer 5aea3269e1 This will bring back the markdown parser to long descriptions 2012-05-14 17:35:02 +02:00
Mike van Riel 3f8a4fca6d fixed autoloading error 2012-04-07 11:49:54 +02:00
Mike van Riel 6a34cf123c fixed error in composer file 2012-04-06 20:39:59 +02:00
10 changed files with 489 additions and 42 deletions
+57
View File
@@ -0,0 +1,57 @@
The ReflectionDocBlock Component
================================
Introduction
------------
The ReflectionDocBlock component of phpDocumentor provides a DocBlock parser
that is 100% compatible with the [PHPDoc standard](http://phpdoc.org/docs/latest).
With this component can a library provide support for annotations via DocBlocks
or otherwise retrieve information that is embedded in a DocBlock.
> **Note**: *this is a core component of phpDocumentor and is constantly being
> optimized for performance.*
Installation
------------
You can install the component in the following ways:
* Use the official Github repository (https://github.com/phpDocumentor/ReflectionDocBlock)
* Via Composer (http://packagist.org/packages/phpdocumentor/reflection-docblock)
Usage
-----
The ReflectionDocBlock component is designed to work in an identical fashion to
PHP's own Reflection extension (http://php.net/manual/en/book.reflection.php).
Parsing can be initiated by instantiating the
`\phpDocumentor\Reflection\DocBlock()` class and passing it a string containing
a DocBlock (including asterisks) or by passing an object supporting the
`getDocComment()` method.
> *Examples of objects having the `getDocComment()` method are the
> `ReflectionClass` and the `ReflectionMethod` classes of the PHP
> Reflection extension*
Example:
$class = new ReflectionClass('MyClass');
$phpdoc = new \phpDocumentor\Reflection\DocBlock($class);
or
$docblock = <<<DOCBLOCK
/**
* This is a short description.
*
* This is a *long* description.
*
* @return void
*/
DOCBLOCK;
$phpdoc = new \phpDocumentor\Reflection\DocBlock($docblock);
+3 -2
View File
@@ -6,9 +6,10 @@
{"name": "Mike van Riel", "email": "[email protected]"}
],
"require": {
"php": ">=5.3.2"
"php": ">=5.3.2",
"dflydev/markdown": "1.0.*"
},
"autoload": {
"psr-0": {"phpDocumentor/Reflection": "src/phpDocumentor"}
"psr-0": {"phpDocumentor\\Reflection": "src/"}
}
}
+116 -11
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
@@ -127,11 +152,11 @@ class DocBlock implements \Reflector
preg_match(
'/(?x)
\A (
[^\n]+
[^\n.]+
(?:
(?! (?<=\.) \n | \n{2} ) # disallow the first seperator here
\n (?! [ \t]* @\pL ) # disallow second seperator
[^\n]+
(?! \. \s | \n{2} ) # disallow the first seperator here
[\n.] (?! [ \t]* @\pL ) # disallow second seperator
[^\n.]+
)*
\.?
)
@@ -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);
@@ -78,7 +78,7 @@ class LongDescription implements \Reflector
);
}
if (class_exists('\dflydev\markdown\MarkdownExtraParser()')) {
if (class_exists('dflydev\markdown\MarkdownExtraParser')) {
$md = new \dflydev\markdown\MarkdownExtraParser();
$result = $md->transformMarkdown($result);
}
@@ -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;
/**
@@ -59,7 +59,7 @@ class Tag implements \Reflector
$tag_name = str_replace(
' ', '', ucwords(str_replace('-', ' ', $matches[1]))
).'Tag';
$class_name = '\\phpDocumentor\\Reflection\\DocBlock\\Tag\\' . $tag_name;
$class_name = 'phpDocumentor\\Reflection\\DocBlock\\Tag\\' . $tag_name;
return (@class_exists($class_name))
? new $class_name($matches[1], isset($matches[2]) ? $matches[2] : '')
@@ -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'),
);
}
}
+103 -2
View File
@@ -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,10 +39,110 @@ 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'));
$this->assertTrue($object->hasTag('return'));
}
public function testDotSeperation()
{
$fixture = <<<DOCBLOCK
/**
* This is a short description. This is a long description.
* This is a continuation of the long description.
*/
DOCBLOCK;
$object = new DocBlock($fixture);
$this->assertEquals(
'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()
);
}
/**
* 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')
);
}
}