mirror of
https://github.com/barryvdh/ReflectionDocBlock.git
synced 2026-08-18 01:57:13 +00:00
Change behaviour of @param parsing
In issue report phpDocumentor/phpDocumentor2#620 @bobef reported that when he used just a Type as content of the @param that it would be recognized as description instead of the Type. According to the unit tests this is correct behaviour but after reviewing the pattern of the output his version is more consistent. As such I have altered the behaviour to act as following: If only one word is found after an @param (word means white-space bounded series of characters) then interpret that as the type and not description. During this item several issues in unit tests were fixed and a new 'Type' Collection was introduced that is capable of expanding types based on a given namespace and series of aliases. This should be re-used in phpDocumentor's Transformer as a duplication exists there with the expanding of the Types. Please note: the suggested format by @bobef is not valid according to the PHPDoc Standard but is provided for convenience.
This commit is contained in:
@@ -327,7 +327,7 @@ class DocBlock implements \Reflector
|
||||
);
|
||||
}
|
||||
|
||||
$namespace = '';
|
||||
$namespace = '\\';
|
||||
if ($this->namespace != 'default' && $this->namespace != 'global') {
|
||||
$namespace = rtrim($this->namespace, '\\') . '\\';
|
||||
}
|
||||
@@ -398,4 +398,16 @@ class DocBlock implements \Reflector
|
||||
return 'Not yet implemented';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
public function getNamespaceAliases()
|
||||
{
|
||||
return $this->namespace_aliases;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use phpDocumentor\Reflection\DocBlock\Tag;
|
||||
class ParamTag extends Tag
|
||||
{
|
||||
/** @var string */
|
||||
protected $type = null;
|
||||
protected $type = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@@ -33,7 +33,7 @@ class ParamTag extends Tag
|
||||
/**
|
||||
* Parses a tag and populates the member variables.
|
||||
*
|
||||
* @param string $type Tag identifier for this tag (should be 'return')
|
||||
* @param string $type Tag identifier for this tag (should be 'param')
|
||||
* @param string $content Contents for this tag.
|
||||
*/
|
||||
public function __construct($type, $content)
|
||||
@@ -42,13 +42,13 @@ class ParamTag extends Tag
|
||||
$this->content = $content;
|
||||
$content = preg_split('/\s+/u', $content);
|
||||
|
||||
// if there is only 1, it is either a piece of content or a variable name
|
||||
if (count($content) > 1) {
|
||||
// if the first item that is encountered is not a variable; it is a type
|
||||
if (isset($content[0]) && (strlen($content[0]) > 0) && ($content[0][0] !== '$')) {
|
||||
$this->type = array_shift($content);
|
||||
}
|
||||
|
||||
// if the next item starts with a $ it must be the variable name
|
||||
if ((strlen($content[0]) > 0) && ($content[0][0] == '$')) {
|
||||
if (isset($content[0]) && (strlen($content[0]) > 0) && ($content[0][0] == '$')) {
|
||||
$this->variableName = array_shift($content);
|
||||
}
|
||||
|
||||
@@ -62,14 +62,13 @@ class ParamTag extends Tag
|
||||
*/
|
||||
public function getTypes()
|
||||
{
|
||||
$types = explode('|', $this->type);
|
||||
foreach ($types as &$type) {
|
||||
$type = !empty($type) && $this->docblock
|
||||
? $this->docblock->expandType($type)
|
||||
: trim($type);
|
||||
}
|
||||
$types = new \phpDocumentor\Reflection\DocBlock\Type\Collection(
|
||||
array($this->type),
|
||||
$this->docblock ? $this->docblock->getNamespace() : null,
|
||||
$this->docblock ? $this->docblock->getNamespaceAliases() : array()
|
||||
);
|
||||
|
||||
return array_values(array_unique($types, SORT_REGULAR));
|
||||
return $types->getArrayCopy();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +78,7 @@ class ParamTag extends Tag
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->docblock->expandType($this->type);
|
||||
return implode('|', $this->getTypes());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
namespace phpDocumentor\Reflection\DocBlock\Type;
|
||||
|
||||
class Collection extends \ArrayObject
|
||||
{
|
||||
/** @var string Definition of the OR operator for types */
|
||||
const OPERATOR_OR = '|';
|
||||
|
||||
/** @var string Definition of the ARRAY operator for types */
|
||||
const OPERATOR_ARRAY = '[]';
|
||||
|
||||
/** @var string Definition of the NAMESPACE operator in PHP */
|
||||
const OPERATOR_NAMESPACE = '\\';
|
||||
|
||||
/** @var string[] List of recognized keywords */
|
||||
protected $keywords = array(
|
||||
'string', 'int', 'integer', 'bool', 'boolean', 'float', 'double',
|
||||
'object', 'mixed', 'array', 'resource', 'void', 'null',
|
||||
'callback', 'callable', 'false', 'true', 'self', '$this', 'static'
|
||||
);
|
||||
|
||||
/**
|
||||
* Current namespace of the invoking location.
|
||||
*
|
||||
* This string is used to prepend to type with a relative location.
|
||||
* May also be 'default' or 'global', in which case they are ignored.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = '\\';
|
||||
|
||||
/**
|
||||
* Associative array of alias => namespace pairs.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $namespace_aliases = array();
|
||||
|
||||
/**
|
||||
* Registers the namespace and aliases; uses that to add and expand the
|
||||
* given types.
|
||||
*
|
||||
* @param string[] $types Array containing a list of types
|
||||
* to add to this container.
|
||||
* @param string|null $namespace The namespace where the types in
|
||||
* this container are relative to; used to expand any relative
|
||||
* namespaces.
|
||||
* @param string[] $namespace_aliases An array containing alias => FQNN
|
||||
* pairs that are used in the resolving process.
|
||||
*/
|
||||
public function __construct(
|
||||
array $types = array(), $namespace = null,
|
||||
array $namespace_aliases = array()
|
||||
) {
|
||||
// only set the namespace if overridden
|
||||
if (is_string($namespace)) {
|
||||
$this->setNamespace($namespace);
|
||||
}
|
||||
$this->namespace_aliases = $namespace_aliases;
|
||||
|
||||
foreach($types as $type) {
|
||||
$this->add($type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace name used to expand relative namespaces with.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the namespace which is used to resolve types with relative
|
||||
* namespaces.
|
||||
*
|
||||
* Unless the name of the current namespace if default or global we make
|
||||
* sure the namespace is succeeded with a '\'. This makes it clear for
|
||||
* the processing functions that a leading slash is present.
|
||||
*
|
||||
* @param string $namespace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
if ($namespace != 'default' && $namespace != 'global') {
|
||||
$namespace = self::OPERATOR_NAMESPACE
|
||||
. trim($namespace, self::OPERATOR_NAMESPACE)
|
||||
. self::OPERATOR_NAMESPACE;
|
||||
} else {
|
||||
$namespace = '\\';
|
||||
}
|
||||
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of namespace aliases used to expand the typed with.
|
||||
*
|
||||
* @return string[] An associative array of Alias => Fully Qualified
|
||||
* Namespace Names.
|
||||
*/
|
||||
public function getNamespaceAliases()
|
||||
{
|
||||
return $this->namespace_aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the namespace aliases to expand the added types.
|
||||
*
|
||||
* @param string[] $namespace_aliases An associative array of Alias => Fully
|
||||
* Qualified Namespace Names.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setNamespaceAliases($namespace_aliases)
|
||||
{
|
||||
$this->namespace_aliases = $namespace_aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new type to the collection and expands it if it contains a
|
||||
* relative namespace.
|
||||
*
|
||||
* If a class in the type contains a relative namespace than this collection
|
||||
* will try to expand that into a FQCN.
|
||||
*
|
||||
* @param string $type A 'Type' as defined in the phpDocumentor
|
||||
* documentation.
|
||||
*
|
||||
* @throws \InvalidArgumentException if a non-string argument is passed.
|
||||
*
|
||||
* @see http://phpdoc.org/docs/latest/for-users/types.html for the
|
||||
* definition of a type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($type)
|
||||
{
|
||||
if (!is_string($type)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'A type should be represented by a string, received: '
|
||||
.var_export($type, true)
|
||||
);
|
||||
}
|
||||
|
||||
// separate the type by the OR operator
|
||||
$type_parts = explode(self::OPERATOR_OR, $type);
|
||||
foreach($type_parts as $part) {
|
||||
$expanded_type = $this->expand($part);
|
||||
if ($expanded_type) {
|
||||
$this[] = $expanded_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes the given type and returns the FQCN variant.
|
||||
*
|
||||
* When a type is provided this method checks whether it is not a keyword or
|
||||
* Fully Qualified Class Name. If so it will use the given namespace and
|
||||
* aliases to expand the type to a FQCN representation.
|
||||
*
|
||||
* This method only works as expected if the namespace and aliases are set;
|
||||
* no dynamic reflection is being performed here.
|
||||
*
|
||||
* @uses getNamespace to determine with what to prefix the type name.
|
||||
* @uses getNamespaceAliases to check whether the first part of the relative
|
||||
* type name should not be replaced with another namespace.
|
||||
*
|
||||
* @param string $type The relative or absolute type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function expand($type)
|
||||
{
|
||||
$type = trim($type);
|
||||
if (!$type) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($this->isTypeAnArray($type)) {
|
||||
return $this->expand(substr($type, 0, -2)).self::OPERATOR_ARRAY;
|
||||
}
|
||||
|
||||
if ($this->isRelativeType($type) && !$this->isTypeAKeyword($type)) {
|
||||
$type_parts = explode(self::OPERATOR_NAMESPACE, $type);
|
||||
|
||||
// if the first segment is not an alias; prepend namespace name and
|
||||
// return
|
||||
if (!isset($this->namespace_aliases[$type_parts[0]])) {
|
||||
return $this->getNamespace() . $type;
|
||||
}
|
||||
|
||||
$type_parts[0] = $this->namespace_aliases[$type_parts[0]];
|
||||
$type = implode(self::OPERATOR_NAMESPACE, $type_parts);
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether the given type represents an array.
|
||||
*
|
||||
* @param string $type A relative or absolute type as defined in the
|
||||
* phpDocumentor documentation.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isTypeAnArray($type)
|
||||
{
|
||||
return (substr($type, -2) == self::OPERATOR_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether the given type represents a PHPDoc keyword.
|
||||
*
|
||||
* @param string $type A relative or absolute type as defined in the
|
||||
* phpDocumentor documentation.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isTypeAKeyword($type)
|
||||
{
|
||||
return in_array(strtolower($type), $this->keywords);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether the given type represents a relative or absolute path.
|
||||
*
|
||||
* This method will detect keywords as being absolute; even though they are
|
||||
* not preceeded by a namespace separator.
|
||||
*
|
||||
* @param string $type A relative or absolute type as defined in the
|
||||
* phpDocumentor documentation.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isRelativeType($type)
|
||||
{
|
||||
return ($type[0] !== self::OPERATOR_NAMESPACE)
|
||||
|| $this->isTypeAKeyword($type);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user