diff --git a/composer.json b/composer.json index 5ab46f7..05b2051 100644 --- a/composer.json +++ b/composer.json @@ -3,20 +3,24 @@ "type": "library", "license": "MIT", "authors": [ - {"name": "Mike van Riel", "email": "mike.vanriel@naenius.com"} + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } ], "require": { - "php": ">=5.3.3" + "php": ">=5.5", + "phpdocumentor/reflection-common": "dev-master@dev" }, "autoload": { - "psr-0": {"phpDocumentor": ["src/"]} + "psr-4": {"phpDocumentor\\Reflection\\": ["src/"]} + }, + "autoload-dev": { + "psr-4": {"phpDocumentor\\Reflection\\": ["tests/unit"]} }, "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "erusev/parsedown": "~1.0", - "league/commonmark": "*" + "phpunit/phpunit": "^4.6", + "mockery/mockery": "^0.9.4" }, "extra": { "branch-alias": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f67ad2a..8b7a6f1 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,5 +10,8 @@ ./src/ + + ./vendor/ + diff --git a/src/DocBlock.php b/src/DocBlock.php new file mode 100644 index 0000000..2b1dd49 --- /dev/null +++ b/src/DocBlock.php @@ -0,0 +1,218 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlock\Context; +use phpDocumentor\Reflection\DocBlock\Location; + +final class DocBlock +{ + /** @var string The opening line for this docblock. */ + private $summary = ''; + + /** @var DocBlock\Description The actual description for this docblock. */ + private $description = null; + + /** @var Tag[] An array containing all the tags in this docblock; except inline. */ + private $tags = array(); + + /** @var Context Information about the context of this DocBlock. */ + private $context = null; + + /** @var Location Information about the location of this DocBlock. */ + private $location = null; + + /** @var bool Is this DocBlock (the start of) a template? */ + private $isTemplateStart = false; + + /** @var bool Does this DocBlock signify the end of a DocBlock template? */ + private $isTemplateEnd = false; + + /** + * Parses the given docblock and populates the member fields. + * + * The constructor may also receive namespace information such as the + * current namespace and aliases. This information is used by some tags + * (e.g. return, param, etc.) to turn a relative Type into a FQCN. + * + * @param string $summary + * @param DocBlock\Description $description + * @param DocBlock\Tag[] $tags + * @param Context $context The context in which the DocBlock occurs. + * @param Location $location The location within the file that this DocBlock occurs in. + * @param bool $isTemplateStart + * @param bool $isTemplateEnd + */ + public function __construct( + $summary = '', + DocBlock\Description $description = null, + array $tags = [], + Context $context = null, + Location $location = null, + $isTemplateStart = false, + $isTemplateEnd = false + ) + { + $this->summary = $summary; + $this->description = $description ?: new DocBlock\Description(''); + foreach ($tags as $tag) { + $this->addTag($tag); + } + + $this->context = $context; + $this->location = $location; + + $this->isTemplateEnd = $isTemplateEnd; + $this->isTemplateStart = $isTemplateStart; + } + + /** + * Returns whether this DocBlock is the start of a Template section. + * + * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker + * (`#@+`) that is appended directly after the opening `/**` of a DocBlock. + * + * An example of such an opening is: + * + * ``` + * /**#@+ + * * My DocBlock + * * / + * ``` + * + * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all + * elements that follow until another DocBlock is found that contains the closing marker (`#@-`). + * + * @see self::isTemplateEnd() for the check whether a closing marker was provided. + * + * @return boolean + */ + public function isTemplateStart() + { + return $this->isTemplateStart; + } + + /** + * Returns whether this DocBlock is the end of a Template section. + * + * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality. + * + * @return boolean + */ + public function isTemplateEnd() + { + return $this->isTemplateEnd; + } + + /** + * Returns the current context. + * + * @return Context + */ + public function getContext() + { + return $this->context; + } + + /** + * Returns the current location. + * + * @return Location + */ + public function getLocation() + { + return $this->location; + } + + /** + * @return string + */ + public function getSummary() + { + return $this->summary; + } + + /** + * @return DocBlock\Description + */ + public function getDescription() + { + return $this->description; + } + + /** + * Adds a tag to this DocBlock. + * + * @param Tag $tag The tag to add. + * + * @return void + */ + public function addTag(Tag $tag) + { + $this->tags[] = $tag; + } + + /** + * Returns the tags for this DocBlock. + * + * @return Tag[] + */ + public function getTags() + { + return $this->tags; + } + + /** + * Returns an array of tags matching the given name. If no tags are found + * an empty array is returned. + * + * @param string $name String to search by. + * + * @return Tag[] + */ + public function getTagsByName($name) + { + $result = array(); + + /** @var Tag $tag */ + foreach ($this->getTags() as $tag) { + if ($tag->getName() != $name) { + continue; + } + + $result[] = $tag; + } + + return $result; + } + + /** + * Checks if a tag of a certain type is present in this DocBlock. + * + * @param string $name Tag name to check for. + * + * @return bool + */ + public function hasTag($name) + { + /** @var Tag $tag */ + foreach ($this->getTags() as $tag) { + if ($tag->getName() == $name) { + return true; + } + } + + return false; + } +} diff --git a/src/DocBlock/Context.php b/src/DocBlock/Context.php deleted file mode 100644 index 8cd179b..0000000 --- a/src/DocBlock/Context.php +++ /dev/null @@ -1,75 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -/** - * Provides information about the Context in which the DocBlock occurs that receives this context. - * - * A DocBlock does not know of its own accord in which namespace it occurs and which namespace aliases are applicable - * for the block of code in which it is in. This information is however necessary to resolve Class names in tags since - * you can provide a short form or make use of namespace aliases. - * - * The phpDocumentor Reflection component knows how to create this class but if you use the DocBlock parser from your - * own application it is possible to generate a Context class using the ContextFactory; this will analyze the file in - * which an associated class resides for its namespace and imports. - * - * @see ContextFactory::createFromClassReflector() - * @see ContextFactory::createForNamespace() - */ -final class Context -{ - /** @var string The current namespace. */ - private $namespace = ''; - - /** @var array List of namespace aliases => Fully Qualified Namespace. */ - private $namespaceAliases = []; - - /** - * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN) - * format (without a preceding `\`). - * - * @param string $namespace The namespace where this DocBlock resides in. - * @param array $namespaceAliases List of namespace aliases => Fully Qualified Namespace. - */ - public function __construct($namespace, array $namespaceAliases = []) - { - $this->namespace = ('global' !== $namespace && 'default' !== $namespace) - ? trim((string)$namespace, '\\') - : ''; - - foreach ($namespaceAliases as $alias => $fqnn) { - $this->namespaceAliases[$alias] = trim((string)$fqnn, '\\'); - } - } - - /** - * Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in. - * - * @return string - */ - public function getNamespace() - { - return $this->namespace; - } - - /** - * Returns a list of Qualified Namespace Names (thus without `\` in front) that are imported, the keys represent - * the alias for the imported Namespace. - * - * @return string[] - */ - public function getNamespaceAliases() - { - return $this->namespaceAliases; - } -} diff --git a/src/DocBlock/ContextFactory.php b/src/DocBlock/ContextFactory.php deleted file mode 100644 index 3b87b97..0000000 --- a/src/DocBlock/ContextFactory.php +++ /dev/null @@ -1,174 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -/** - * Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor. - * - * For a DocBlock to be able to resolve types that use partial namespace names or rely on namespace imports we need to - * provide a bit of context so that the DocBlock can read that and based on it decide how to resolve the types to - * Fully Qualified names. - * - * @see Context for more information. - */ -final class ContextFactory -{ - /** The literal used at the end of a use statement. */ - const T_LITERAL_END_OF_USE = ';'; - - /** The literal used between sets of use statements */ - const T_LITERAL_USE_SEPARATOR = ','; - - /** - * Build a Context given a Class Reflection. - * - * @param \ReflectionClass $class - * - * @see Context for more information on Contexts. - * - * @return Context - */ - public function createFromClassReflector(\ReflectionClass $class) - { - return $this->createForNamespace( - $class->getNamespaceName(), - file_get_contents($class->getFileName()) - ); - } - - /** - * Build a Context for a namespace in the provided file contents. - * - * @param string $namespace It does not matter if a `\` precedes the namespace name, this method first normalizes. - * @param string $fileContents the file's contents to retrieve the aliases from with the given namespace. - * - * @see Context for more information on Contexts. - * - * @return Context - */ - public function createForNamespace($namespace, $fileContents) - { - $namespace = trim($namespace, '\\'); - $useStatements = []; - $currentNamespace = ''; - $tokens = new \ArrayIterator(token_get_all($fileContents)); - - while ($tokens->valid()) { - switch ($tokens->current()[0]) { - case T_NAMESPACE: - $currentNamespace = $this->parseNamespace($tokens); - break; - case T_USE: - if ($currentNamespace === $namespace) { - $useStatements = array_merge($useStatements, $this->parseUseStatement($tokens)); - } - break; - } - $tokens->next(); - } - - return new Context($namespace, $useStatements); - } - - /** - * Deduce the name from tokens when we are at the T_NAMESPACE token. - * - * @param \ArrayIterator $tokens - * - * @return string - */ - private function parseNamespace(\ArrayIterator $tokens) - { - // skip to the first string or namespace separator - $this->skipToNextStringOrNamespaceSeparator($tokens); - - $name = ''; - while ($tokens->valid() && ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) - ) { - $name .= $tokens->current()[1]; - $tokens->next(); - } - - return $name; - } - - /** - * Deduce the names of all imports when we are at the T_USE token. - * - * @param \ArrayIterator $tokens - * - * @return string[] - */ - private function parseUseStatement(\ArrayIterator $tokens) - { - $uses = []; - $continue = true; - - while ($continue) { - $this->skipToNextStringOrNamespaceSeparator($tokens); - - list($alias, $fqnn) = $this->extractUseStatement($tokens); - $uses[$alias] = $fqnn; - if ($tokens->current()[0] === self::T_LITERAL_END_OF_USE) { - $continue = false; - } - } - - return $uses; - } - - /** - * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token. - * - * @param \ArrayIterator $tokens - * - * @return void - */ - private function skipToNextStringOrNamespaceSeparator(\ArrayIterator $tokens) - { - while ($tokens->valid() && ($tokens->current()[0] !== T_STRING) && ($tokens->current()[0] !== T_NS_SEPARATOR)) { - $tokens->next(); - } - } - - /** - * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of - * a USE statement yet. - * - * @param \ArrayIterator $tokens - * - * @return string - */ - private function extractUseStatement(\ArrayIterator $tokens) - { - $result = ['']; - while ($tokens->valid() - && ($tokens->current()[0] !== self::T_LITERAL_USE_SEPARATOR) - && ($tokens->current()[0] !== self::T_LITERAL_END_OF_USE) - ) { - if ($tokens->current()[0] === T_AS) { - $result[] = ''; - } - if ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) { - $result[count($result) - 1] .= $tokens->current()[1]; - } - $tokens->next(); - } - - if (count($result) == 1) { - $result[] = substr($result[0], strrpos($result[0], '\\') + 1); - } - - return array_reverse($result); - } -} diff --git a/src/DocBlock/Description.php b/src/DocBlock/Description.php new file mode 100644 index 0000000..1e5703b --- /dev/null +++ b/src/DocBlock/Description.php @@ -0,0 +1,50 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock\Description\Formatter; +use phpDocumentor\Reflection\DocBlock\Description\PassthroughFormatter; + + +class Description +{ + /** @var Tag[]|string[] The contents, as an array of strings and Tag objects */ + private $tokens; + + /** + * Initializes a this object with a series of tokens of which a description consists. + * + * @param Tag[]|string[] $tokens + */ + public function __construct(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * Renders this description as a string where the provided formatter will format tags for the expected output. + * + * @param Formatter|null $formatter + * + * @return string + */ + public function render(Formatter $formatter = null) + { + if ($formatter === null) { + $formatter = new PassthroughFormatter(); + } + + return $formatter->format($this->tokens); + } + +} diff --git a/src/Types/Integer.php b/src/DocBlock/Description/Formatter.php similarity index 57% rename from src/Types/Integer.php rename to src/DocBlock/Description/Formatter.php index be4555e..db43c38 100644 --- a/src/Types/Integer.php +++ b/src/DocBlock/Description/Formatter.php @@ -10,19 +10,18 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\Types; +namespace phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\DocBlock\Tag; -final class Integer implements Type +interface Formatter { /** - * Returns a rendered output of the Type as it would be used in a DocBlock. + * Formats the given series of tokens so to form a readable string and return that. + * + * @param string[]|Tag[] $tokens * * @return string */ - public function __toString() - { - return 'int'; - } + public function format(array $tokens); } diff --git a/src/DocBlock/Description/PassthroughFormatter.php b/src/DocBlock/Description/PassthroughFormatter.php new file mode 100644 index 0000000..6dae599 --- /dev/null +++ b/src/DocBlock/Description/PassthroughFormatter.php @@ -0,0 +1,35 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Description; + +use phpDocumentor\Reflection\DocBlock\Tag; + +class PassthroughFormatter implements Formatter +{ + /** + * Formats the given series of tokens so to form a readable string and return that. + * + * @param string[]|Tag[] $tokens + * + * @return string + */ + public function format(array $tokens) + { + $result = ''; + foreach ($tokens as $token) { + $result .= $token instanceof Tag ? '{' . (string)$token . '}' : $token; + } + + return $result; + } +} diff --git a/src/DocBlock/DescriptionFactory.php b/src/DocBlock/DescriptionFactory.php new file mode 100644 index 0000000..9bcf057 --- /dev/null +++ b/src/DocBlock/DescriptionFactory.php @@ -0,0 +1,110 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +final class DescriptionFactory +{ + /** @var TagFactory */ + private $tagFactory; + + /** + * Initializes this factory with the means to construct (inline) tags. + * + * @param TagFactory $tagFactory + */ + public function __construct(TagFactory $tagFactory) + { + $this->tagFactory = $tagFactory; + } + + /** + * Returns the parsed text of this description. + * + * @param string $contents + * @param Context $context + * + * @return array An array of strings and tag objects, in the order they occur within the description. + */ + public function create($contents, Context $context = null) + { + return new Description($this->parse($this->lex($contents), $context)); + } + + /** + * @param $contents + * @return array + */ + private function lex($contents) + { + // performance optimalization; if there is no inline tag, don't bother splitting it up. + if (strpos($contents, '{@') === false) { + return [$contents]; + } + + return preg_split( + '/\{ + # "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally. + (?!@\}) + # We want to capture the whole tag line, but without the inline tag delimiters. + (\@ + # Match everything up to the next delimiter. + [^{}]* + # Nested inline tag content should not be captured, or it will appear in the result separately. + (?: + # Match nested inline tags. + (?: + # Because we did not catch the tag delimiters earlier, we must be explicit with them here. + # Notice that this also matches "{}", as a way to later introduce it as an escape sequence. + \{(?1)?\} + | + # Make sure we match hanging "{". + \{ + ) + # Match content after the nested inline tag. + [^{}]* + )* # If there are more inline tags, match them as well. We use "*" since there may not be any + # nested inline tags. + ) + \}/Sux', + $contents, + null, + PREG_SPLIT_DELIM_CAPTURE + ); + } + + /** + * Parses the stream of tokens in to a new set of tokens containing Tags. + * + * @param string[] $tokens + * @param Context $context + * + * @return string[]|Tag[] + */ + private function parse($tokens, Context $context) + { + $count = count($tokens); + for ($i = 1; $i < $count; $i += 2) { + $tokens[$i] = $this->tagFactory->create($tokens[$i], $context); + } + + //In order to allow "literal" inline tags, the otherwise invalid + //sequence "{@}" is changed to "@", and "{}" is changed to "}". + //See unit tests for examples. + for ($i = 0; $i < $count; $i += 2) { + $tokens[$i] = str_replace(['{@}', '{}'], ['@', '}'], $tokens[$i]); + } + + return $tokens; + } + +} diff --git a/src/DocBlock/ExampleFinder.php b/src/DocBlock/ExampleFinder.php new file mode 100644 index 0000000..3cc5dab --- /dev/null +++ b/src/DocBlock/ExampleFinder.php @@ -0,0 +1,170 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +use phpDocumentor\Reflection\DocBlock\Tags\Example; + +/** + * Class used to find an example file's location based on a given ExampleDescriptor. + */ +class ExampleFinder +{ + /** @var string */ + private $sourceDirectory = ''; + + /** @var string[] */ + private $exampleDirectories = array(); + + /** + * Attempts to find the example contents for the given descriptor. + * + * @param Example $example + * + * @return string + */ + public function find(Example $example) + { + $filename = $example->getFilePath(); + + $file = $this->getExampleFileContents($filename); + if (!$file) { + return "** File not found : {$filename} **"; + } + + return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount())); + } + + /** + * Registers the project's root directory where an 'examples' folder can be expected. + * + * @param string $directory + * + * @return void + */ + public function setSourceDirectory($directory = '') + { + $this->sourceDirectory = $directory; + } + + /** + * Returns the project's root directory where an 'examples' folder can be expected. + * + * @return string + */ + public function getSourceDirectory() + { + return $this->sourceDirectory; + } + + /** + * Registers a series of directories that may contain examples. + * + * @param string[] $directories + */ + public function setExampleDirectories(array $directories) + { + $this->exampleDirectories = $directories; + } + + /** + * Returns a series of directories that may contain examples. + * + * @return string[] + */ + public function getExampleDirectories() + { + return $this->exampleDirectories; + } + + /** + * Attempts to find the requested example file and returns its contents or null if no file was found. + * + * This method will try several methods in search of the given example file, the first one it encounters is + * returned: + * + * 1. Iterates through all examples folders for the given filename + * 2. Checks the source folder for the given filename + * 3. Checks the 'examples' folder in the current working directory for examples + * 4. Checks the path relative to the current working directory for the given filename + * + * @param string $filename + * + * @return string|null + */ + private function getExampleFileContents($filename) + { + $normalizedPath = null; + + foreach ($this->exampleDirectories as $directory) { + $exampleFileFromConfig = $this->constructExamplePath($directory, $filename); + if (is_readable($exampleFileFromConfig)) { + $normalizedPath = $exampleFileFromConfig; + break; + } + } + + if (!$normalizedPath) { + if (is_readable($this->getExamplePathFromSource($filename))) { + $normalizedPath = $this->getExamplePathFromSource($filename); + } elseif (is_readable($this->getExamplePathFromExampleDirectory($filename))) { + $normalizedPath = $this->getExamplePathFromExampleDirectory($filename); + } elseif (is_readable($filename)) { + $normalizedPath = $filename; + } + } + + return $normalizedPath && is_readable($normalizedPath) ? file($normalizedPath) : null; + } + + /** + * Get example filepath based on the example directory inside your project. + * + * @param string $file + * + * @return string + */ + private function getExamplePathFromExampleDirectory($file) + { + return getcwd() . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $file; + } + + /** + * Returns a path to the example file in the given directory.. + * + * @param string $directory + * @param string $file + * + * @return string + */ + private function constructExamplePath($directory, $file) + { + return rtrim($directory, '\\/') . DIRECTORY_SEPARATOR . $file; + } + + /** + * Get example filepath based on sourcecode. + * + * @param string $file + * + * @return string + */ + private function getExamplePathFromSource($file) + { + return sprintf( + '%s%s%s', + trim($this->getSourceDirectory(), '\\/'), + DIRECTORY_SEPARATOR, + trim($file, '"') + ); + } +} diff --git a/src/phpDocumentor/Reflection/DocBlock/Serializer.php b/src/DocBlock/Serializer.php similarity index 94% rename from src/phpDocumentor/Reflection/DocBlock/Serializer.php rename to src/DocBlock/Serializer.php index 1fb01ec..84da3f7 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Serializer.php +++ b/src/DocBlock/Serializer.php @@ -1,11 +1,11 @@ - * @copyright 2013 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 */ @@ -59,9 +59,9 @@ class Serializer /** * Sets the string to indent comments with. - * + * * @param string $indentString The string to indent comments with. - * + * * @return $this This serializer object. */ public function setIndentationString($indentString) @@ -73,7 +73,7 @@ class Serializer /** * Gets the string to indent comments with. - * + * * @return string The indent string. */ public function getIndentationString() @@ -83,9 +83,9 @@ class Serializer /** * Sets the number of indents. - * + * * @param int $indent The number of times the indent string is repeated. - * + * * @return $this This serializer object. */ public function setIndent($indent) @@ -96,7 +96,7 @@ class Serializer /** * Gets the number of indents. - * + * * @return int The number of times the indent string is repeated. */ public function getIndent() @@ -106,12 +106,12 @@ class Serializer /** * Sets whether or not the first line should be indented. - * + * * Sets whether or not the first line (the one with the "/**") should be * indented. - * + * * @param bool $indentFirstLine The new value for this setting. - * + * * @return $this This serializer object. */ public function setIsFirstLineIndented($indentFirstLine) @@ -122,7 +122,7 @@ class Serializer /** * Gets whether or not the first line should be indented. - * + * * @return bool Whether or not the first line should be indented. */ public function isFirstLineIndented() @@ -132,13 +132,13 @@ class Serializer /** * Sets the line length. - * + * * Sets the length of each line in the serialization. Content will be * wrapped within this limit. - * + * * @param int|null $lineLength The length of each line. NULL to disable line * wrapping altogether. - * + * * @return $this This serializer object. */ public function setLineLength($lineLength) @@ -149,7 +149,7 @@ class Serializer /** * Gets the line length. - * + * * @return int|null The length of each line or NULL if line wrapping is * disabled. */ @@ -162,7 +162,7 @@ class Serializer * Generate a DocBlock comment. * * @param DocBlock The DocBlock to serialize. - * + * * @return string The serialized doc block. */ public function getDocComment(DocBlock $docblock) diff --git a/src/DocBlock/Tag.php b/src/DocBlock/Tag.php new file mode 100644 index 0000000..fa9e2a3 --- /dev/null +++ b/src/DocBlock/Tag.php @@ -0,0 +1,93 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock; + +/** + * Parses a tag definition for a DocBlock. + */ +class Tag +{ + /** @var string Name of the tag */ + protected $name = ''; + + /** @var Description|null Description of the tag. */ + protected $description; + + /** + * Parses a tag and populates the member variables. + * + * We explicitly do not type-hint the $description so that classes inheriting this class can override the + * constructor without running into PHP notices. + * + * @param string $name Name of the tag. + * @param Description $description The contents of the given tag. + */ + public function __construct($name, $description) + { + $this->validateTagName($name); + if (!$description instanceof Description) { + throw new \InvalidArgumentException('The description should be an object of type Description'); + } + + $this->name = $name; + $this->description = $description; + } + + /** + * Gets the name of this tag. + * + * @return string The name of this tag. + */ + public function getName() + { + return $this->name; + } + + public function render(DocBlock\Description\Formatter $formatter = null) + { + if (!$formatter) { + $formatter = new DocBlock\Description\PassthroughFormatter(); + } + + return $formatter->format([$this]); + } + + /** + * Returns the tag as a serialized string + * + * @return string + */ + public function __toString() + { + return "@{$this->getName()} {$this->description->render()}"; + } + + /** + * Validates if the tag name matches the expected format, otherwise throws an exception. + * + * @param string $name + * + * @return void + */ + private function validateTagName($name) + { + if (!preg_match('/^' . TagFactory::REGEX_TAGNAME . '$/u', $name)) { + throw new \InvalidArgumentException( + 'The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, ' + . 'hyphens and backslashes.' + ); + } + } +} diff --git a/src/DocBlock/TagFactory.php b/src/DocBlock/TagFactory.php new file mode 100644 index 0000000..fc2c2c4 --- /dev/null +++ b/src/DocBlock/TagFactory.php @@ -0,0 +1,145 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\FqsenFactory; + +final class TagFactory +{ + /** PCRE regular expression matching a tag name. */ + const REGEX_TAGNAME = '[\w\-\_\\\\]+'; + + /** + * @var array An array with a tag as a key, and an FQCN to a class that handles it as an array value. + */ + private $tagHandlerMappings = array( + 'author' => '\phpDocumentor\Reflection\DocBlock\Tags\Author', + 'covers' => '\phpDocumentor\Reflection\DocBlock\Tags\Covers', + 'deprecated' => '\phpDocumentor\Reflection\DocBlock\Tags\Deprecated', + 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example', + 'link' => '\phpDocumentor\Reflection\DocBlock\Tags\Link', + 'method' => '\phpDocumentor\Reflection\DocBlock\Tags\Method', + 'param' => '\phpDocumentor\Reflection\DocBlock\Tags\Param', + 'property-read' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead', + 'property' => '\phpDocumentor\Reflection\DocBlock\Tags\Property', + 'property-write' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite', + 'return' => '\phpDocumentor\Reflection\DocBlock\Tags\Return', + 'see' => '\phpDocumentor\Reflection\DocBlock\Tags\See', + 'since' => '\phpDocumentor\Reflection\DocBlock\Tags\Since', + 'source' => '\phpDocumentor\Reflection\DocBlock\Tags\Source', + 'throw' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws', + 'throws' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws', + 'uses' => '\phpDocumentor\Reflection\DocBlock\Tags\Uses', + 'var' => '\phpDocumentor\Reflection\DocBlock\Tags\Var_', + 'version' => '\phpDocumentor\Reflection\DocBlock\Tags\Version' + ); + + /** @var FqsenFactory */ + private $fqsenFactory; + + public function __construct(FqsenFactory $fqsenFactory) + { + $this->fqsenFactory = $fqsenFactory; + } + + /** + * Factory method responsible for instantiating the correct sub type. + * + * @param string $tagLine The text for this tag, including description. + * @param Context $context + * + * @throws \InvalidArgumentException if an invalid tag line was presented. + * + * @return static A new tag object. + */ + public function create($tagLine, Context $context = null) + { + if (!$context) { + $context = new Context(''); + } + list($tagName, $tagDescription) = $this->extractTagParts($tagLine); + + $handler = Tag::class; + if (isset($this->tagHandlerMappings[$tagName])) { + $handler = $this->tagHandlerMappings[$tagName]; + } elseif ($this->isAnnotation($tagName)) { + $tagName = (string)$this->fqsenFactory->create($tagName, $context); + if (isset($this->tagHandlerMappings[$tagName])) { + $handler = $this->tagHandlerMappings[$tagName]; + } + } + + return $handler::create($tagName, $tagDescription); + } + + /** + * Registers a handler for tags. + * + * Registers a handler for tags. The class specified is autoloaded if it's not available. It must inherit from + * this class. + * + * @param string $tag Name of tag to register a handler for. When registering a namespaced tag, the full + * name, along with a prefixing slash MUST be provided. + * @param string|null $handler FQCN of handler. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function registerTagHandler($tag, $handler) + { + $tag = trim((string)$tag); + + if ('' !== $tag + && class_exists($handler) + && is_subclass_of($handler, Tag::class) + && !strpos($tag, '\\') //Accept no slash, and 1st slash at offset 0. + ) { + $this->tagHandlerMappings[$tag] = $handler; + return true; + } + + return false; + } + + /** + * Extracts all components for a tag. + * + * @param string $tagLine + * + * @return string[] + */ + private function extractTagParts($tagLine) + { + $matches = array(); + if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)?/us', $tagLine, $matches)) { + throw new \InvalidArgumentException( + 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' + ); + } + + if (count($matches) == 1) { + $matches[] = ''; + } + + return $matches; + } + + private function isAnnotation($tag) + { + // 1. Contains a namespace separator + // 2. Contains parenthesis + // 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part + // of the annotation class name matches the found tag name + + return false; + } +} diff --git a/src/DocBlock/Tags/Author.php b/src/DocBlock/Tags/Author.php new file mode 100644 index 0000000..9b3195d --- /dev/null +++ b/src/DocBlock/Tags/Author.php @@ -0,0 +1,96 @@ + + * @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\Tag; + +/** + * Reflection class for an {@}author tag in a Docblock. + */ +final class Author extends Tag +{ + /** @var string register that this is the author tag. */ + protected $name = 'author'; + + /** @var string The name of the author */ + private $authorName = ''; + + /** @var string The email of the author */ + private $authorEmail = ''; + + /** + * Initializes this tag with the author name and e-mail. + * + * @param string $authorName + * @param string $authorEmail + */ + public function __construct($authorName, $authorEmail) + { + if (!is_string($authorName)) { + throw new \InvalidArgumentException('The author tag does not have a valid name'); + } + if (!is_string($authorEmail) || ($authorEmail && !filter_var($authorEmail, FILTER_VALIDATE_EMAIL))) { + throw new \InvalidArgumentException('The author tag does not have a valid e-mail address'); + } + + $this->authorName = $authorName; + $this->authorEmail = $authorEmail; + } + + /** + * Gets the author's name. + * + * @return string The author's name. + */ + public function getAuthorName() + { + return $this->authorName; + } + + /** + * Returns the author's email. + * + * @return string The author's email. + */ + public function getEmail() + { + return $this->authorEmail; + } + + /** + * Returns this tag in string form. + * + * @return string + */ + public function __toString() + { + return $this->authorName . '<' . $this->authorEmail . '>'; + } + + /** + * {@inheritdoc} + */ + public static function create($content) + { + $splitTagContent = preg_match('/^([^\<]*)(?:\<([^\>]*)\>)?$/u', $content, $matches); + if (!$splitTagContent) { + return null; + } + + $authorName = trim($matches[1]); + $email = isset($matches[2]) ? trim($matches[2]) : ''; + + return new static($authorName, $email); + } +} diff --git a/src/DocBlock/Tags/Covers.php b/src/DocBlock/Tags/Covers.php new file mode 100644 index 0000000..d3e0b20 --- /dev/null +++ b/src/DocBlock/Tags/Covers.php @@ -0,0 +1,78 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\Fqsen; +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\Context; +use DocBlock\Types\Resolver; + +/** + * Reflection class for a @covers tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class Covers extends Tag +{ + /** @var Fqsen */ + protected $refers = null; + + /** + * Initializes this tag. + * + * @param Fqsen $refers + * @param Description $description + */ + public function __construct(Fqsen $refers, Description $description) + { + $this->refers = $refers; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create($content, Context $context) + { + $parts = preg_split('/\s+/Su', $content, 2); + $resolver = new Resolver(); + + return new static( + $resolver->resolve($parts[0], $context), + new Description(isset($parts[1]) ? $parts[1] : '', $context) + ); + } + + /** + * Returns the structural element this tag refers to. + * + * @return Fqsen + */ + public function getReference() + { + return $this->refers; + } + + /** + * Returns a string representation of this tag. + * + * @return string + */ + public function __toString() + { + return $this->refers . ' ' . $this->description->render(); + } +} diff --git a/src/DocBlock/Tags/Deprecated.php b/src/DocBlock/Tags/Deprecated.php new file mode 100644 index 0000000..f998fa8 --- /dev/null +++ b/src/DocBlock/Tags/Deprecated.php @@ -0,0 +1,84 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Context; +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a {@}deprecated tag in a Docblock. + */ +final class Deprecated extends Tag +{ + /** + * PCRE regular expression matching a version vector. + * Assumes the "x" modifier. + */ + const REGEX_VECTOR = '(?: + # Normal release vectors. + \d\S* + | + # VCS version vectors. Per PHPCS, they are expected to + # follow the form of the VCS name, followed by ":", followed + # by the version vector itself. + # By convention, popular VCSes like CVS, SVN and GIT use "$" + # around the actual version vector. + [^\s\:]+\:\s*\$[^\$]+\$ + )'; + + /** @var string The version vector. */ + private $version = ''; + + public function __construct($version, Description $description = null) + { + $this->version = $version; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create($content, Context $context = null) + { + $matches = []; + if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $content, $matches)) { + return null; + } + + return new static( + $matches[1], + new Description(isset($matches[2]) ? $matches[2] : '', $context) + ); + } + + /** + * Gets the version section of the tag. + * + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return $this->version . ' ' . $this->description->render(); + } +} diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/ExampleTag.php b/src/DocBlock/Tags/Example.php similarity index 90% rename from src/phpDocumentor/Reflection/DocBlock/Tag/ExampleTag.php rename to src/DocBlock/Tags/Example.php index f22be26..9a91a2b 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/ExampleTag.php +++ b/src/DocBlock/Tags/Example.php @@ -10,7 +10,7 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Tag; @@ -21,9 +21,9 @@ use phpDocumentor\Reflection\DocBlock\Tag; * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ -class ExampleTag extends SourceTag +class Example extends Source { - /** + /** * @var string Path to a file to use as an example. * May also be an absolute URI. */ @@ -40,7 +40,7 @@ class ExampleTag extends SourceTag */ public function getContent() { - if (null === $this->content) { + if (null === $this->description) { $filePath = '"' . $this->filePath . '"'; if ($this->isURI) { $filePath = $this->isUriRelative($this->filePath) @@ -48,10 +48,10 @@ class ExampleTag extends SourceTag :$this->filePath; } - $this->content = $filePath . ' ' . parent::getContent(); + $this->description = $filePath . ' ' . parent::getContent(); } - return $this->content; + return $this->description; } /** @@ -87,7 +87,7 @@ class ExampleTag extends SourceTag } else { $this->setDescription(''); } - $this->content = $content; + $this->description = $content; } return $this; @@ -103,12 +103,12 @@ class ExampleTag extends SourceTag { return $this->filePath; } - + /** * Sets the file path. - * + * * @param string $filePath The new file path to use for the example. - * + * * @return $this */ public function setFilePath($filePath) @@ -116,18 +116,18 @@ class ExampleTag extends SourceTag $this->isURI = false; $this->filePath = trim($filePath); - $this->content = null; + $this->description = null; return $this; } - + /** * Sets the file path as an URI. - * + * * This function is equivalent to {@link setFilePath()}, except that it * converts an URI to a file path before that. - * + * * There is no getFileURI(), as {@link getFilePath()} is compatible. - * + * * @param string $uri The new file URI to use as an example. * * @return $this @@ -135,7 +135,7 @@ class ExampleTag extends SourceTag public function setFileURI($uri) { $this->isURI = true; - $this->content = null; + $this->description = null; $this->filePath = $this->isUriRelative($uri) ? rawurldecode(str_replace(array('/', '\\'), '%2F', $uri)) diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/LinkTag.php b/src/DocBlock/Tags/Link.php similarity index 73% rename from src/phpDocumentor/Reflection/DocBlock/Tag/LinkTag.php rename to src/DocBlock/Tags/Link.php index f79f25d..329b2ee 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/LinkTag.php +++ b/src/DocBlock/Tags/Link.php @@ -10,18 +10,14 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Tag; /** * Reflection class for a @link tag in a Docblock. - * - * @author Ben Selby - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org */ -class LinkTag extends Tag +class Link extends Tag { /** @var string */ protected $link = ''; @@ -31,11 +27,11 @@ class LinkTag extends Tag */ public function getContent() { - if (null === $this->content) { - $this->content = "{$this->link} {$this->description}"; + if (null === $this->description) { + $this->description = "{$this->link} {$this->description}"; } - return $this->content; + return $this->description; } /** @@ -50,7 +46,7 @@ class LinkTag extends Tag $this->setDescription(isset($parts[1]) ? $parts[1] : $parts[0]); - $this->content = $content; + $this->description = $content; return $this; } @@ -75,7 +71,7 @@ class LinkTag extends Tag { $this->link = $link; - $this->content = null; + $this->description = null; return $this; } } diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/MethodTag.php b/src/DocBlock/Tags/Method.php similarity index 92% rename from src/phpDocumentor/Reflection/DocBlock/Tag/MethodTag.php rename to src/DocBlock/Tags/Method.php index 7a5ce79..d4f5b6e 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/MethodTag.php +++ b/src/DocBlock/Tags/Method.php @@ -10,7 +10,7 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Tag; @@ -21,7 +21,7 @@ use phpDocumentor\Reflection\DocBlock\Tag; * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ -class MethodTag extends ReturnTag +class Method extends Return_ { /** @var string */ @@ -29,7 +29,7 @@ class MethodTag extends ReturnTag /** @var string */ protected $arguments = ''; - + /** @var bool */ protected $isStatic = false; @@ -38,17 +38,17 @@ class MethodTag extends ReturnTag */ public function getContent() { - if (null === $this->content) { - $this->content = ''; + if (null === $this->description) { + $this->description = ''; if ($this->isStatic) { - $this->content .= 'static '; + $this->description .= 'static '; } - $this->content .= $this->type . + $this->description .= $this->type . " {$this->method_name}({$this->arguments}) " . $this->description; } - return $this->content; + return $this->description; } /** @@ -130,7 +130,7 @@ class MethodTag extends ReturnTag { $this->method_name = $method_name; - $this->content = null; + $this->description = null; return $this; } @@ -155,7 +155,7 @@ class MethodTag extends ReturnTag { $this->arguments = $arguments; - $this->content = null; + $this->description = null; return $this; } @@ -180,10 +180,10 @@ class MethodTag extends ReturnTag return $arguments; } - + /** * Checks whether the method tag describes a static method or not. - * + * * @return bool TRUE if the method declaration is for a static method, FALSE * otherwise. */ @@ -191,19 +191,19 @@ class MethodTag extends ReturnTag { return $this->isStatic; } - + /** * Sets a new value for whether the method is static or not. - * + * * @param bool $isStatic The new value to set. - * + * * @return $this */ public function setIsStatic($isStatic) { $this->isStatic = $isStatic; - $this->content = null; + $this->description = null; return $this; } } diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/ParamTag.php b/src/DocBlock/Tags/Param.php similarity index 91% rename from src/phpDocumentor/Reflection/DocBlock/Tag/ParamTag.php rename to src/DocBlock/Tags/Param.php index 9bc0270..15814ab 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/ParamTag.php +++ b/src/DocBlock/Tags/Param.php @@ -10,7 +10,7 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Tag; @@ -21,7 +21,7 @@ use phpDocumentor\Reflection\DocBlock\Tag; * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ -class ParamTag extends ReturnTag +class Param extends Return_ { /** @var string */ protected $variableName = ''; @@ -34,11 +34,11 @@ class ParamTag extends ReturnTag */ public function getContent() { - if (null === $this->content) { - $this->content + if (null === $this->description) { + $this->description = "{$this->type} {$this->variableName} {$this->description}"; } - return $this->content; + return $this->description; } /** * {@inheritdoc} @@ -78,7 +78,7 @@ class ParamTag extends ReturnTag $this->setDescription(implode('', $parts)); - $this->content = $content; + $this->description = $content; return $this; } @@ -103,7 +103,7 @@ class ParamTag extends ReturnTag { $this->variableName = $name; - $this->content = null; + $this->description = null; return $this; } diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyTag.php b/src/DocBlock/Tags/Property.php similarity index 59% rename from src/phpDocumentor/Reflection/DocBlock/Tag/PropertyTag.php rename to src/DocBlock/Tags/Property.php index 288ecff..9846e16 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyTag.php +++ b/src/DocBlock/Tags/Property.php @@ -10,15 +10,11 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; /** * Reflection class for a @property tag in a Docblock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org */ -class PropertyTag extends ParamTag +class Property extends Param { } diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyReadTag.php b/src/DocBlock/Tags/PropertyRead.php similarity index 59% rename from src/phpDocumentor/Reflection/DocBlock/Tag/PropertyReadTag.php rename to src/DocBlock/Tags/PropertyRead.php index 3340602..e53436a 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyReadTag.php +++ b/src/DocBlock/Tags/PropertyRead.php @@ -10,15 +10,11 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; /** * Reflection class for a @property-read tag in a Docblock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org */ -class PropertyReadTag extends PropertyTag +class PropertyRead extends Property { } diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyWriteTag.php b/src/DocBlock/Tags/PropertyWrite.php similarity index 59% rename from src/phpDocumentor/Reflection/DocBlock/Tag/PropertyWriteTag.php rename to src/DocBlock/Tags/PropertyWrite.php index ec4e866..5cd3f62 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyWriteTag.php +++ b/src/DocBlock/Tags/PropertyWrite.php @@ -10,15 +10,11 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; /** * Reflection class for a @property-write tag in a Docblock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org */ -class PropertyWriteTag extends PropertyTag +class PropertyWrite extends Property { } diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/ReturnTag.php b/src/DocBlock/Tags/Return_.php similarity index 81% rename from src/phpDocumentor/Reflection/DocBlock/Tag/ReturnTag.php rename to src/DocBlock/Tags/Return_.php index 9293db9..b6a765f 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/ReturnTag.php +++ b/src/DocBlock/Tags/Return_.php @@ -10,23 +10,19 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Type\Collection; /** * Reflection class for a @return tag in a Docblock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org */ -class ReturnTag extends Tag +class Return_ extends Tag { /** @var string The raw type component. */ protected $type = ''; - + /** @var Collection The parsed type component. */ protected $types = null; @@ -35,11 +31,11 @@ class ReturnTag extends Tag */ public function getContent() { - if (null === $this->content) { - $this->content = "{$this->type} {$this->description}"; + if (null === $this->description) { + $this->description = "{$this->type} {$this->description}"; } - return $this->content; + return $this->description; } /** @@ -57,7 +53,7 @@ class ReturnTag extends Tag $this->setDescription(isset($parts[1]) ? $parts[1] : ''); - $this->content = $content; + $this->description = $content; return $this; } @@ -83,7 +79,7 @@ class ReturnTag extends Tag /** * Returns the type collection. - * + * * @return void */ protected function getTypesCollection() diff --git a/src/DocBlock/Tags/See.php b/src/DocBlock/Tags/See.php new file mode 100644 index 0000000..159afb2 --- /dev/null +++ b/src/DocBlock/Tags/See.php @@ -0,0 +1,74 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use DocBlock\Types\Resolver; +use phpDocumentor\Reflection\Fqsen; +use phpDocumentor\Reflection\DocBlock\Context; +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for an {@}see tag in a Docblock. + */ +class See extends Tag +{ + /** @var Fqsen */ + protected $refers = null; + + /** + * Initializes this tag. + * + * @param Fqsen $refers + * @param Description $description + */ + public function __construct(Fqsen $refers, Description $description) + { + $this->refers = $refers; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create($content, Context $context) + { + $parts = preg_split('/\s+/Su', $content, 2); + $resolver = new Resolver(); + + return new static( + $resolver->resolve($parts[0], $context), + new Description(isset($parts[1]) ? $parts[1] : '', $context) + ); + } + + /** + * Returns the structural element this tag refers to. + * + * @return Fqsen + */ + public function getReference() + { + return $this->refers; + } + + /** + * Returns a string representation of this tag. + * + * @return string + */ + public function __toString() + { + return $this->refers . ' ' . $this->description->render(); + } +} diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/SinceTag.php b/src/DocBlock/Tags/Since.php similarity index 54% rename from src/phpDocumentor/Reflection/DocBlock/Tag/SinceTag.php rename to src/DocBlock/Tags/Since.php index ba009c4..e2e526b 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/SinceTag.php +++ b/src/DocBlock/Tags/Since.php @@ -10,17 +10,11 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; - -use phpDocumentor\Reflection\DocBlock\Tag\VersionTag; +namespace phpDocumentor\Reflection\DocBlock\Tags; /** * Reflection class for a @since tag in a Docblock. - * - * @author Vasil Rangelov - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org */ -class SinceTag extends VersionTag +class Since extends Version { } diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/SourceTag.php b/src/DocBlock/Tags/Source.php similarity index 85% rename from src/phpDocumentor/Reflection/DocBlock/Tag/SourceTag.php rename to src/DocBlock/Tags/Source.php index 3400220..841416a 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/SourceTag.php +++ b/src/DocBlock/Tags/Source.php @@ -10,18 +10,14 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; use phpDocumentor\Reflection\DocBlock\Tag; /** * Reflection class for a @source tag in a Docblock. - * - * @author Vasil Rangelov - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org */ -class SourceTag extends Tag +class Source extends Tag { /** * @var int The starting line, relative to the structural element's @@ -29,7 +25,7 @@ class SourceTag extends Tag */ protected $startingLine = 1; - /** + /** * @var int|null The number of lines, relative to the starting line. NULL * means "to the end". */ @@ -40,12 +36,12 @@ class SourceTag extends Tag */ public function getContent() { - if (null === $this->content) { - $this->content + if (null === $this->description) { + $this->description = "{$this->startingLine} {$this->lineCount} {$this->description}"; } - return $this->content; + return $this->description; } /** @@ -75,7 +71,7 @@ class SourceTag extends Tag $this->lineCount = (int)$matches[2]; } $this->setDescription($matches[3]); - $this->content = $content; + $this->description = $content; } return $this; @@ -94,17 +90,17 @@ class SourceTag extends Tag /** * Sets the starting line. - * + * * @param int $startingLine The new starting line, relative to the * structural element's location. - * + * * @return $this */ public function setStartingLine($startingLine) { $this->startingLine = $startingLine; - $this->content = null; + $this->description = null; return $this; } @@ -121,17 +117,17 @@ class SourceTag extends Tag /** * Sets the number of lines. - * + * * @param int|null $lineCount The new number of lines, relative to the * starting line. NULL means "to the end". - * + * * @return $this */ public function setLineCount($lineCount) { $this->lineCount = $lineCount; - $this->content = null; + $this->description = null; return $this; } } diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTag.php b/src/DocBlock/Tags/Throws.php similarity index 59% rename from src/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTag.php rename to src/DocBlock/Tags/Throws.php index 58ee44a..36fd32d 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTag.php +++ b/src/DocBlock/Tags/Throws.php @@ -10,15 +10,11 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; /** * Reflection class for a @throws tag in a Docblock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org */ -class ThrowsTag extends ReturnTag +class Throws extends Return_ { } diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/UsesTag.php b/src/DocBlock/Tags/Uses.php similarity index 60% rename from src/phpDocumentor/Reflection/DocBlock/Tag/UsesTag.php rename to src/DocBlock/Tags/Uses.php index da0d663..58dcf07 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/UsesTag.php +++ b/src/DocBlock/Tags/Uses.php @@ -10,15 +10,11 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; /** * Reflection class for a @uses tag in a Docblock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org */ -class UsesTag extends SeeTag +class Uses extends See { } diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/VarTag.php b/src/DocBlock/Tags/Var_.php similarity index 60% rename from src/phpDocumentor/Reflection/DocBlock/Tag/VarTag.php rename to src/DocBlock/Tags/Var_.php index 236b2c8..489d070 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/VarTag.php +++ b/src/DocBlock/Tags/Var_.php @@ -10,15 +10,11 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; /** * Reflection class for a @var tag in a Docblock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org */ -class VarTag extends ParamTag +class Var_ extends Param { } diff --git a/src/DocBlock/Tags/Version.php b/src/DocBlock/Tags/Version.php new file mode 100644 index 0000000..ef6ccaf --- /dev/null +++ b/src/DocBlock/Tags/Version.php @@ -0,0 +1,82 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @version tag in a Docblock. + */ +class Version extends Tag +{ + /** + * PCRE regular expression matching a version vector. + * Assumes the "x" modifier. + */ + const REGEX_VECTOR = '(?: + # Normal release vectors. + \d\S* + | + # VCS version vectors. Per PHPCS, they are expected to + # follow the form of the VCS name, followed by ":", followed + # by the version vector itself. + # By convention, popular VCSes like CVS, SVN and GIT use "$" + # around the actual version vector. + [^\s\:]+\:\s*\$[^\$]+\$ + )'; + + /** @var string The version vector. */ + private $version = ''; + + public function __construct($version, Description $description = null) + { + $this->version = $version; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create($content, Context $context = null) + { + $matches = []; + if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $content, $matches)) { + return null; + } + + return new static( + $matches[1], + new Description(isset($matches[2]) ? $matches[2] : '', $context) + ); + } + + /** + * Gets the version section of the tag. + * + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return $this->version . ' ' . $this->description->render(); + } +} diff --git a/src/DocBlockFactory.php b/src/DocBlockFactory.php new file mode 100644 index 0000000..ad04866 --- /dev/null +++ b/src/DocBlockFactory.php @@ -0,0 +1,260 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\DocBlock\TagFactory; + +final class DocBlockFactory implements DocBlockFactoryInterface +{ + /** @var DocBlock\DescriptionFactory */ + private $descriptionFactory; + + /** @var DocBlock\TagFactory */ + private $tagFactory; + + /** + * Initializes this factory with the required subcontractors. + * + * @param DocBlock\DescriptionFactory $descriptionFactory + * @param DocBlock\TagFactory $tagFactory + */ + public function __construct( + DocBlock\DescriptionFactory $descriptionFactory, + DocBlock\TagFactory $tagFactory + ) + { + $this->descriptionFactory = $descriptionFactory; + $this->tagFactory = $tagFactory; + } + + /** + * Factory method for easy instantiation. + * + * @param string[] $additionalTags + * + * @return DocBlockFactory + */ + public static function createInstance(array $additionalTags = []) + { + $tagFactory = new TagFactory(new FqsenFactory()); + foreach ($additionalTags as $tagName => $tagClassName) { + $tagFactory->registerTagHandler($tagName, $tagClassName); + } + + return new self(new DescriptionFactory($tagFactory), $tagFactory); + } + + /** + * @param $docblock + * @param DocBlock\Context $context + * @param DocBlock\Location $location + * + * @return DocBlock + */ + public function create($docblock, DocBlock\Context $context = null, DocBlock\Location $location = null) + { + if (is_object($docblock)) { + if (!method_exists($docblock, 'getDocComment')) { + throw new \InvalidArgumentException( + 'Invalid object passed; the given object must support the getDocComment method' + ); + } + + $docblock = $docblock->getDocComment(); + } + + $parts = $this->splitDocBlock($this->stripDocComment($docblock)); + list($templateMarker, $summary, $description, $tags) = $parts; + + return new DocBlock( + $summary, + new DocBlock\Description($description), + $this->parseTagBlock($tags), + $context, + $location, + $templateMarker === '#@+', + $templateMarker === '#@-' + ); + } + + /** + * Strips the asterisks from the DocBlock comment. + * + * @param string $comment String containing the comment text. + * + * @return string + */ + private function stripDocComment($comment) + { + $comment = trim( + preg_replace( + '#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', + '$1', + $comment + ) + ); + + // reg ex above is not able to remove */ from a single line docblock + if (substr($comment, -2) == '*/') { + $comment = trim(substr($comment, 0, -2)); + } + + // normalize strings + $comment = str_replace(array("\r\n", "\r"), "\n", $comment); + + return $comment; + } + + /** + * Splits the DocBlock into a template marker, summary, description and block of tags. + * + * @param string $comment Comment to split into the sub-parts. + * + * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. + * @author Mike van Riel for extending the regex with template marker support. + * + * @return string[] containing the template marker (if any), summary, description and a string containing the tags. + */ + private function splitDocBlock($comment) + { + // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This + // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the + // performance impact of running a regular expression + if (strpos($comment, '@') === 0) { + return array('', '', '', $comment); + } + + // clears all extra horizontal whitespace from the line endings to prevent parsing issues + $comment = preg_replace('/\h*$/Sum', '', $comment); + + /* + * Splits the docblock into a template marker, summary, description and tags section. + * + * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may + * occur after it and will be stripped). + * - The short description is started from the first character until a dot is encountered followed by a + * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing + * errors). This is optional. + * - The long description, any character until a new line is encountered followed by an @ and word + * characters (a tag). This is optional. + * - Tags; the remaining characters + * + * Big thanks to RichardJ for contributing this Regular Expression + */ + preg_match( + '/ + \A + # 1. Extract the template marker + (?:(\#\@\+|\#\@\-)\n?)? + + # 2. Extract the summary + (?: + (?! @\pL ) # The summary may not start with an @ + ( + [^\n.]+ + (?: + (?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines + [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line + [^\n.]+ # Include anything else + )* + \.? + )? + ) + + # 3. Extract the description + (?: + \s* # Some form of whitespace _must_ precede a description because a summary must be there + (?! @\pL ) # The description may not start with an @ + ( + [^\n]+ + (?: \n+ + (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line + [^\n]+ # Include anything else + )* + ) + )? + + # 4. Extract the tags (anything that follows) + (\s+ [\s\S]*)? # everything that follows + /ux', + $comment, + $matches + ); + array_shift($matches); + + while (count($matches) < 4) { + $matches[] = ''; + } + + return $matches; + } + + /** + * Creates the tag objects. + * + * @param string $tags Tag block to parse. + * + * @return DocBlock\Tag[] + */ + private function parseTagBlock($tags) + { + $tags = $this->filterTagBlock($tags); + if (!$tags) { + return []; + } + + $result = $this->splitTagBlockIntoTagLines($tags); + foreach ($result as $key => $tagLine) { + $result[$key] = $this->tagFactory->create(trim($tagLine)); + } + + return $result; + } + + /** + * @param string $tags + * + * @return string[] + */ + private function splitTagBlockIntoTagLines($tags) + { + $result = array(); + foreach (explode("\n", $tags) as $tag_line) { + if (isset($tag_line[0]) && ($tag_line[0] === '@')) { + $result[] = $tag_line; + } else { + $result[count($result) - 1] .= "\n" . $tag_line; + } + } + return $result; + } + + /** + * @param $tags + * @return string + */ + private function filterTagBlock($tags) + { + $tags = trim($tags); + if (!$tags) { + return null; + } + + if ('@' !== $tags[0]) { + throw new \LogicException('A tag block started with text instead of an at-sign(@): ' . $tags); + } + + return $tags; + } +} diff --git a/src/Type.php b/src/Type.php deleted file mode 100644 index 33ca559..0000000 --- a/src/Type.php +++ /dev/null @@ -1,18 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection; - -interface Type -{ - public function __toString(); -} diff --git a/src/Types/Array_.php b/src/Types/Array_.php deleted file mode 100644 index cf5d7aa..0000000 --- a/src/Types/Array_.php +++ /dev/null @@ -1,87 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Fqsen; -use phpDocumentor\Reflection\Type; - -/** - * Represents an array type as described in the PSR-5, the PHPDoc Standard. - * - * An array can be represented in two forms: - * - * 1. Untyped (`array`), where the key and value type is unknown and hence classified as 'Mixed'. - * 2. Types (`string[]`), where the value type is provided by preceding an opening and closing square bracket with a - * type name. - */ -final class Array_ implements Type -{ - /** @var Type */ - private $valueType; - - /** @var Type */ - private $keyType; - - /** - * Initializes this representation of an array with the given Type or Fqsen. - * - * @param Type $valueType - * @param Type $keyType - */ - public function __construct(Type $valueType = null, Type $keyType = null) - { - if ($keyType === null) { - $keyType = new Mixed(); - } - if ($valueType === null) { - $valueType = new Mixed(); - } - - $this->valueType = $valueType; - $this->keyType = $keyType; - } - - /** - * Returns the type for the keys of this array. - * - * @return Type - */ - public function getKeyType() - { - return $this->keyType; - } - - /** - * Returns the value for the keys of this array. - * - * @return Type - */ - public function getValueType() - { - return $this->valueType; - } - - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - if ($this->valueType instanceof Mixed) { - return 'array'; - } - - return $this->valueType . '[]'; - } -} diff --git a/src/Types/Boolean.php b/src/Types/Boolean.php deleted file mode 100644 index f82b19e..0000000 --- a/src/Types/Boolean.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a Boolean type. - */ -final class Boolean implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'bool'; - } -} diff --git a/src/Types/Callable_.php b/src/Types/Callable_.php deleted file mode 100644 index 68ebfbd..0000000 --- a/src/Types/Callable_.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a Callable type. - */ -final class Callable_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'callable'; - } -} diff --git a/src/Types/Compound.php b/src/Types/Compound.php deleted file mode 100644 index ac099c8..0000000 --- a/src/Types/Compound.php +++ /dev/null @@ -1,82 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a Compound Type. - * - * A Compound Type is not so much a special keyword or object reference but is a series of Types that are separated - * using an OR operator (`|`). This combination of types signifies that whatever is associated with this compound type - * may contain a value with any of the given types. - */ -final class Compound implements Type -{ - /** @var Type[] */ - private $types = []; - - /** - * Initializes a compound type (i.e. `string|int`) and tests if the provided types all implement the Type interface. - * - * @param Type[] $types - */ - public function __construct($types) - { - foreach ($types as $type) { - if (!$type instanceof Type) { - throw new \InvalidArgumentException('A compound type can only have other types as elements'); - } - } - - $this->types = $types; - } - - /** - * Returns the type at the given index. - * - * @param integer $index - * - * @return Type|null - */ - public function get($index) - { - if (!$this->has($index)) { - return null; - } - - return $this->types[$index]; - } - - /** - * Tests if this compound type has a type with the given index. - * - * @param integer $index - * - * @return bool - */ - public function has($index) - { - return isset($this->types[$index]); - } - - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return implode('|', $this->types); - } -} diff --git a/src/Types/Float.php b/src/Types/Float.php deleted file mode 100644 index bc32978..0000000 --- a/src/Types/Float.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a Float. - */ -final class Float implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'float'; - } -} diff --git a/src/Types/Mixed.php b/src/Types/Mixed.php deleted file mode 100644 index 79695f4..0000000 --- a/src/Types/Mixed.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing an unknown, or mixed, type. - */ -final class Mixed implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'mixed'; - } -} diff --git a/src/Types/Null_.php b/src/Types/Null_.php deleted file mode 100644 index 203b422..0000000 --- a/src/Types/Null_.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a null value or type. - */ -final class Null_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'null'; - } -} diff --git a/src/Types/Object_.php b/src/Types/Object_.php deleted file mode 100644 index b337c71..0000000 --- a/src/Types/Object_.php +++ /dev/null @@ -1,70 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Fqsen; -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing an object. - * - * An object can be either typed or untyped. When an object is typed it means that it has an identifier, the FQSEN, - * pointing to an element in PHP. Object types that are untyped do not refer to a specific class but represent objects - * in general. - */ -final class Object_ implements Type -{ - /** @var Fqsen|null */ - private $fqsen; - - /** - * Initializes this object with an optional FQSEN, if not provided this object is considered 'untyped'. - * - * @param Fqsen $fqsen - */ - public function __construct(Fqsen $fqsen = null) - { - if (strpos((string)$fqsen, '::') !== false || strpos((string)$fqsen, '()') !== false) { - throw new \InvalidArgumentException( - 'Object types can only refer to a class, interface or trait but a method, function, constant or ' - . 'property was received: ' . (string)$fqsen - ); - } - - $this->fqsen = $fqsen; - } - - /** - * Returns the FQSEN associated with this object. - * - * @return Fqsen|null - */ - public function getFqsen() - { - return $this->fqsen; - } - - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - if ($this->fqsen) { - return (string)$this->fqsen; - } - - return 'object'; - } -} diff --git a/src/Types/Resolver.php b/src/Types/Resolver.php deleted file mode 100644 index 024c65e..0000000 --- a/src/Types/Resolver.php +++ /dev/null @@ -1,276 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\DocBlock\Context; -use phpDocumentor\Reflection\Type; - -final class Resolver -{ - /** @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 and unto which Value Object they map */ - private $keywords = array( - 'string' => 'phpDocumentor\Reflection\Types\String', - 'int' => 'phpDocumentor\Reflection\Types\Integer', - 'integer' => 'phpDocumentor\Reflection\Types\Integer', - 'bool' => 'phpDocumentor\Reflection\Types\Boolean', - 'boolean' => 'phpDocumentor\Reflection\Types\Boolean', - 'float' => 'phpDocumentor\Reflection\Types\Float', - 'double' => 'phpDocumentor\Reflection\Types\Float', - 'object' => 'phpDocumentor\Reflection\Types\Object_', - 'mixed' => 'phpDocumentor\Reflection\Types\Mixed', - 'array' => 'phpDocumentor\Reflection\Types\Array_', - 'resource' => 'phpDocumentor\Reflection\Types\Resource', - 'void' => 'phpDocumentor\Reflection\Types\Void', - 'null' => 'phpDocumentor\Reflection\Types\Null_', - 'scalar' => 'phpDocumentor\Reflection\Types\Scalar', - 'callback' => 'phpDocumentor\Reflection\Types\Callable_', - 'callable' => 'phpDocumentor\Reflection\Types\Callable_', - 'false' => 'phpDocumentor\Reflection\Types\Boolean', - 'true' => 'phpDocumentor\Reflection\Types\Boolean', - 'self' => 'phpDocumentor\Reflection\Types\Self_', - '$this' => 'phpDocumentor\Reflection\Types\This', - 'static' => 'phpDocumentor\Reflection\Types\Static_' - ); - - /** - * 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. - * - * @param string $type The relative or absolute type. - * - * @uses Context::getNamespace() to determine with what to prefix the type name. - * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be - * replaced with another namespace. - * - * @return Type|null - */ - public function resolve($type, Context $context) - { - if (!is_string($type)) { - throw new \InvalidArgumentException( - 'Attempted to resolve type but it appeared not to be a string, received: ' . var_export($type, true) - ); - } - - $type = trim($type); - if (!$type) { - throw new \InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty'); - } - - switch (true) { - case $this->isKeyword($type): - return $this->resolveKeyword($type); - case ($this->isCompoundType($type)): - return $this->resolveCompoundType($type, $context); - case $this->isFqsen($type): - return $this->resolveFqsen($type); - case $this->isTypedArray($type): - return $this->resolveTypedArray($type, $context); - case $this->isPartialStructuralElementName($type): - return $this->resolvePartialStructuralElementName($type, $context); - // @codeCoverageIgnoreStart - default: - // I haven't got the foggiest how the logic would come here but added this as a defense. - throw new \RuntimeException( - 'Unable to resolve type "' . $type . '", there is no known method to resolve it' - ); - } - // @codeCoverageIgnoreEnd - } - - /** - * Adds a keyword to the list of Keywords and associates it with a specific Value Object. - * - * @param string $keyword - * @param string $typeClassName - * - * @return void - */ - public function addKeyword($keyword, $typeClassName) - { - if (!class_exists($typeClassName)) { - throw new \InvalidArgumentException( - 'The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class' - . ' but we could not find the class ' . $typeClassName - ); - } - - if (!in_array(Type::class, class_implements($typeClassName))) { - throw new \InvalidArgumentException( - 'The class "' . $typeClassName . '" must implement the interface "phpDocumentor\Reflection\Type"' - ); - } - - $this->keywords[$keyword] = $typeClassName; - } - - /** - * Detects whether the given type represents an array. - * - * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. - * - * @return bool - */ - private function isTypedArray($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 - */ - private function isKeyword($type) - { - return in_array(strtolower($type), array_keys($this->keywords), true); - } - - /** - * Detects whether the given type represents a relative structural element name. - * - * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. - * - * @return bool - */ - private function isPartialStructuralElementName($type) - { - return ($type[0] !== self::OPERATOR_NAMESPACE) && !$this->isKeyword($type); - } - - /** - * Tests whether the given type is a Fully Qualified Structural Element Name. - * - * @param string $type - * - * @return bool - */ - private function isFqsen($type) - { - return strpos($type, self::OPERATOR_NAMESPACE) === 0; - } - - /** - * Tests whether the given type is a compound type (i.e. `string|int`). - * - * @param string $type - * - * @return bool - */ - private function isCompoundType($type) - { - return strpos($type, '|') !== false; - } - - /** - * Resolves the given typed array string (i.e. `string[]`) into an Array object with the right types set. - * - * @param string $type - * @param Context $context - * - * @return Array_ - */ - private function resolveTypedArray($type, Context $context) - { - return new Array_($this->resolve(substr($type, 0, -2), $context)); - } - - /** - * Resolves the given keyword (such as `string`) into a Type object representing that keyword. - * - * @param string $type - * - * @return Type - */ - private function resolveKeyword($type) - { - $className = $this->keywords[strtolower($type)]; - - return new $className(); - } - - /** - * Resolves the given FQSEN string into an FQSEN object. - * - * @param string $type - * - * @return Object_ - */ - private function resolveFqsen($type) - { - return new Object_(new Fqsen($type)); - } - - /** - * Resolves a partial Structural Element Name (i.e. `Reflection\DocBlock`) to its FQSEN representation - * (i.e. `\phpDocumentor\Reflection\DocBlock`) based on the Namespace and aliases mentioned in the Context. - * - * @param string $type - * @param Context $context - * - * @return Object_ - */ - private function resolvePartialStructuralElementName($type, Context $context) - { - $typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2); - - $namespaceAliases = $context->getNamespaceAliases(); - - // if the first segment is not an alias; prepend namespace name and return - if (!isset($namespaceAliases[$typeParts[0]])) { - $namespace = $context->getNamespace(); - if ('' !== $namespace) { - $namespace .= self::OPERATOR_NAMESPACE; - } - - return new Object_(new Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type)); - } - - $typeParts[0] = $namespaceAliases[$typeParts[0]]; - - return new Object_(new Fqsen(self::OPERATOR_NAMESPACE . implode(self::OPERATOR_NAMESPACE, $typeParts))); - } - - /** - * Resolves a compound type (i.e. `string|int`) into the appropriate Type objects or FQSEN. - * - * @param string $type - * @param Context $context - * - * @return Compound - */ - private function resolveCompoundType($type, Context $context) - { - $types = []; - - foreach (explode('|', $type) as $part) { - $types[] = $this->resolve($part, $context); - } - - return new Compound($types); - } -} diff --git a/src/Types/Resource.php b/src/Types/Resource.php deleted file mode 100644 index 2c2526b..0000000 --- a/src/Types/Resource.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the 'resource' Type. - */ -final class Resource implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'resource'; - } -} diff --git a/src/Types/Scalar.php b/src/Types/Scalar.php deleted file mode 100644 index 1e2a660..0000000 --- a/src/Types/Scalar.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean. - */ -final class Scalar implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'scalar'; - } -} diff --git a/src/Types/Self_.php b/src/Types/Self_.php deleted file mode 100644 index 1ba3fc5..0000000 --- a/src/Types/Self_.php +++ /dev/null @@ -1,33 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the 'self' type. - * - * Self, as a Type, represents the class in which the associated element was defined. - */ -final class Self_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'self'; - } -} diff --git a/src/Types/Static_.php b/src/Types/Static_.php deleted file mode 100644 index 9eb6729..0000000 --- a/src/Types/Static_.php +++ /dev/null @@ -1,38 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the 'static' type. - * - * Self, as a Type, represents the class in which the associated element was called. This differs from self as self does - * not take inheritance into account but static means that the return type is always that of the class of the called - * element. - * - * See the documentation on late static binding in the PHP Documentation for more information on the difference between - * static and self. - */ -final class Static_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'static'; - } -} diff --git a/src/Types/String.php b/src/Types/String.php deleted file mode 100644 index ad2c842..0000000 --- a/src/Types/String.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the type 'string'. - */ -final class String implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'string'; - } -} diff --git a/src/Types/This.php b/src/Types/This.php deleted file mode 100644 index c098a93..0000000 --- a/src/Types/This.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the '$this' pseudo-type. - * - * $this, as a Type, represents the instance of the class associated with the element as it was called. $this is - * commonly used when documenting fluent interfaces since it represents that the same object is returned. - */ -final class This implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return '$this'; - } -} diff --git a/src/Types/Void.php b/src/Types/Void.php deleted file mode 100644 index 6a6156b..0000000 --- a/src/Types/Void.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the pseudo-type 'void'. - * - * Void is generally only used when working with return types as it signifies that the method intentionally does not - * return any value. - */ -final class Void implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'void'; - } -} diff --git a/src/phpDocumentor/Reflection/DocBlock.php b/src/phpDocumentor/Reflection/DocBlock.php deleted file mode 100644 index b5ac729..0000000 --- a/src/phpDocumentor/Reflection/DocBlock.php +++ /dev/null @@ -1,463 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection; - -use phpDocumentor\Reflection\DocBlock\Tag; -use phpDocumentor\Reflection\DocBlock\Context; -use phpDocumentor\Reflection\DocBlock\Location; - -/** - * Parses the DocBlock for any structure. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class DocBlock implements \Reflector -{ - /** @var string The opening line for this docblock. */ - protected $short_description = ''; - - /** - * @var DocBlock\Description The actual - * description for this docblock. - */ - protected $long_description = null; - - /** - * @var Tag[] An array containing all - * the tags in this docblock; except inline. - */ - protected $tags = array(); - - /** @var Context Information about the context of this DocBlock. */ - protected $context = null; - - /** @var Location Information about the location of this DocBlock. */ - protected $location = null; - - /** @var bool Is this DocBlock (the start of) a template? */ - protected $isTemplateStart = false; - - /** @var bool Does this DocBlock signify the end of a DocBlock template? */ - protected $isTemplateEnd = false; - - /** - * Parses the given docblock and populates the member fields. - * - * The constructor may also receive namespace information such as the - * current namespace and aliases. This information is used by some tags - * (e.g. return, param, etc.) to turn a relative Type into a FQCN. - * - * @param \Reflector|string $docblock A docblock comment (including asterisks) or reflector supporting the - * getDocComment method. - * @param Context $context The context in which the DocBlock occurs. - * @param Location $location The location within the file that this DocBlock occurs in. - * - * @throws \InvalidArgumentException if the given argument does not have the getDocComment method. - */ - public function __construct( - $docblock, - Context $context = null, - Location $location = null - ) { - if (is_object($docblock)) { - if (!method_exists($docblock, 'getDocComment')) { - throw new \InvalidArgumentException( - 'Invalid object passed; the given reflector must support the getDocComment method' - ); - } - - $docblock = $docblock->getDocComment(); - } - - $docblock = $this->cleanInput($docblock); - - list($templateMarker, $short, $long, $tags) = $this->splitDocBlock($docblock); - $this->isTemplateStart = $templateMarker === '#@+'; - $this->isTemplateEnd = $templateMarker === '#@-'; - $this->short_description = $short; - $this->long_description = new DocBlock\Description($long, $this); - $this->parseTags($tags); - - $this->context = $context; - $this->location = $location; - } - - /** - * Strips the asterisks from the DocBlock comment. - * - * @param string $comment String containing the comment text. - * - * @return string - */ - protected function cleanInput($comment) - { - $comment = trim( - preg_replace( - '#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', - '$1', - $comment - ) - ); - - // reg ex above is not able to remove */ from a single line docblock - if (substr($comment, -2) == '*/') { - $comment = trim(substr($comment, 0, -2)); - } - - // normalize strings - $comment = str_replace(array("\r\n", "\r"), "\n", $comment); - - return $comment; - } - - /** - * Splits the DocBlock into a template marker, summary, description and block of tags. - * - * @param string $comment Comment to split into the sub-parts. - * - * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. - * @author Mike van Riel for extending the regex with template marker support. - * - * @return string[] containing the template marker (if any), summary, description and a string containing the tags. - */ - protected function splitDocBlock($comment) - { - // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This - // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the - // performance impact of running a regular expression - if (strpos($comment, '@') === 0) { - return array('', '', '', $comment); - } - - // clears all extra horizontal whitespace from the line endings to prevent parsing issues - $comment = preg_replace('/\h*$/Sum', '', $comment); - - /* - * Splits the docblock into a template marker, short description, long description and tags section - * - * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may - * occur after it and will be stripped). - * - The short description is started from the first character until a dot is encountered followed by a - * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing - * errors). This is optional. - * - The long description, any character until a new line is encountered followed by an @ and word - * characters (a tag). This is optional. - * - Tags; the remaining characters - * - * Big thanks to RichardJ for contributing this Regular Expression - */ - preg_match( - '/ - \A - # 1. Extract the template marker - (?:(\#\@\+|\#\@\-)\n?)? - - # 2. Extract the summary - (?: - (?! @\pL ) # The summary may not start with an @ - ( - [^\n.]+ - (?: - (?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines - [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line - [^\n.]+ # Include anything else - )* - \.? - )? - ) - - # 3. Extract the description - (?: - \s* # Some form of whitespace _must_ precede a description because a summary must be there - (?! @\pL ) # The description may not start with an @ - ( - [^\n]+ - (?: \n+ - (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line - [^\n]+ # Include anything else - )* - ) - )? - - # 4. Extract the tags (anything that follows) - (\s+ [\s\S]*)? # everything that follows - /ux', - $comment, - $matches - ); - array_shift($matches); - - while (count($matches) < 4) { - $matches[] = ''; - } - - return $matches; - } - - /** - * Creates the tag objects. - * - * @param string $tags Tag block to parse. - * - * @return void - */ - protected function parseTags($tags) - { - $result = array(); - $tags = trim($tags); - if ('' !== $tags) { - if ('@' !== $tags[0]) { - throw new \LogicException( - 'A tag block started with text instead of an actual tag,' - . ' this makes the tag block invalid: ' . $tags - ); - } - foreach (explode("\n", $tags) as $tag_line) { - if (isset($tag_line[0]) && ($tag_line[0] === '@')) { - $result[] = $tag_line; - } else { - $result[count($result) - 1] .= "\n" . $tag_line; - } - } - - // create proper Tag objects - foreach ($result as $key => $tag_line) { - $result[$key] = Tag::createInstance(trim($tag_line), $this); - } - } - - $this->tags = $result; - } - - /** - * Gets the text portion of the doc block. - * - * Gets the text portion (short and long description combined) of the doc - * block. - * - * @return string The text portion of the doc block. - */ - public function getText() - { - $short = $this->getShortDescription(); - $long = $this->getLongDescription()->getContents(); - - if ($long) { - return "{$short}\n\n{$long}"; - } else { - return $short; - } - } - - /** - * Set the text portion of the DocBlock. - * - * Sets the text portion (short and long description combined) of the DocBlock. - * - * @param string $comment The new text portion of the DocBlock. - * - * @return $this - */ - public function setText($comment) - { - list(,$short, $long) = $this->splitDocBlock($comment); - $this->short_description = $short; - $this->long_description = new DocBlock\Description($long, $this); - return $this; - } - /** - * Returns the opening line or also known as short description. - * - * @return string - */ - public function getShortDescription() - { - return $this->short_description; - } - - /** - * Returns the full description or also known as long description. - * - * @return DocBlock\Description - */ - public function getLongDescription() - { - return $this->long_description; - } - - /** - * Returns whether this DocBlock is the start of a Template section. - * - * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker - * (`#@+`) that is appended directly after the opening `/**` of a DocBlock. - * - * An example of such an opening is: - * - * ``` - * /**#@+ - * * My DocBlock - * * / - * ``` - * - * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all - * elements that follow until another DocBlock is found that contains the closing marker (`#@-`). - * - * @see self::isTemplateEnd() for the check whether a closing marker was provided. - * - * @return boolean - */ - public function isTemplateStart() - { - return $this->isTemplateStart; - } - - /** - * Returns whether this DocBlock is the end of a Template section. - * - * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality. - * - * @return boolean - */ - public function isTemplateEnd() - { - return $this->isTemplateEnd; - } - - /** - * Returns the current context. - * - * @return Context - */ - public function getContext() - { - return $this->context; - } - - /** - * Returns the current location. - * - * @return Location - */ - public function getLocation() - { - return $this->location; - } - - /** - * Returns the tags for this DocBlock. - * - * @return Tag[] - */ - public function getTags() - { - return $this->tags; - } - - /** - * Returns an array of tags matching the given name. If no tags are found - * an empty array is returned. - * - * @param string $name String to search by. - * - * @return Tag[] - */ - public function getTagsByName($name) - { - $result = array(); - - /** @var Tag $tag */ - foreach ($this->getTags() as $tag) { - if ($tag->getName() != $name) { - continue; - } - - $result[] = $tag; - } - - return $result; - } - - /** - * Checks if a tag of a certain type is present in this DocBlock. - * - * @param string $name Tag name to check for. - * - * @return bool - */ - public function hasTag($name) - { - /** @var Tag $tag */ - foreach ($this->getTags() as $tag) { - if ($tag->getName() == $name) { - return true; - } - } - - return false; - } - - /** - * Appends a tag at the end of the list of tags. - * - * @param Tag $tag The tag to add. - * - * @return Tag The newly added tag. - * - * @throws \LogicException When the tag belongs to a different DocBlock. - */ - public function appendTag(Tag $tag) - { - if (null === $tag->getDocBlock()) { - $tag->setDocBlock($this); - } - - if ($tag->getDocBlock() === $this) { - $this->tags[] = $tag; - } else { - throw new \LogicException( - 'This tag belongs to a different DocBlock object.' - ); - } - - return $tag; - } - - - /** - * Builds a string representation of this object. - * - * @todo determine the exact format as used by PHP Reflection and - * implement it. - * - * @return string - * @codeCoverageIgnore Not yet implemented - */ - public static function export() - { - throw new \Exception('Not yet implemented'); - } - - /** - * Returns the exported information (we should use the export static method - * BUT this throws an exception at this point). - * - * @return string - * @codeCoverageIgnore Not yet implemented - */ - public function __toString() - { - return 'Not yet implemented'; - } -} diff --git a/src/phpDocumentor/Reflection/DocBlock/Description.php b/src/phpDocumentor/Reflection/DocBlock/Description.php deleted file mode 100644 index 0377d62..0000000 --- a/src/phpDocumentor/Reflection/DocBlock/Description.php +++ /dev/null @@ -1,226 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -use phpDocumentor\Reflection\DocBlock; - -/** - * Parses a Description of a DocBlock or tag. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class Description implements \Reflector -{ - /** @var string */ - protected $contents = ''; - - /** @var array|null The contents, as an array of strings and Tag objects or null if it is not parsed yet. */ - protected $parsedContents = null; - - /** @var DocBlock The DocBlock which this description belongs to. */ - protected $docblock = null; - - /** - * Populates the fields of a description. - * - * @param string $content The description's content. - * @param DocBlock $docblock The DocBlock which this description belongs to. - */ - public function __construct($content, DocBlock $docblock = null) - { - $this->setContent($content)->setDocBlock($docblock); - } - - /** - * Gets the text of this description. - * - * @return string - */ - public function getContents() - { - return $this->contents; - } - - /** - * Sets the text of this description. - * - * @param string $content The new text of this description. - * - * @return $this - */ - public function setContent($content) - { - $this->contents = trim($content); - - $this->parsedContents = null; - return $this; - } - - /** - * Returns the parsed text of this description. - * - * @return array An array of strings and tag objects, in the order they - * occur within the description. - */ - public function getParsedContents() - { - if (null === $this->parsedContents) { - $this->parsedContents = preg_split( - '/\{ - # "{@}" is not a valid inline tag. This ensures that - # we do not treat it as one, but treat it literally. - (?!@\}) - # We want to capture the whole tag line, but without the - # inline tag delimiters. - (\@ - # Match everything up to the next delimiter. - [^{}]* - # Nested inline tag content should not be captured, or - # it will appear in the result separately. - (?: - # Match nested inline tags. - (?: - # Because we did not catch the tag delimiters - # earlier, we must be explicit with them here. - # Notice that this also matches "{}", as a way - # to later introduce it as an escape sequence. - \{(?1)?\} - | - # Make sure we match hanging "{". - \{ - ) - # Match content after the nested inline tag. - [^{}]* - )* # If there are more inline tags, match them as well. - # We use "*" since there may not be any nested inline - # tags. - ) - \}/Sux', - $this->contents, - null, - PREG_SPLIT_DELIM_CAPTURE - ); - - $count = count($this->parsedContents); - for ($i=1; $i<$count; $i += 2) { - $this->parsedContents[$i] = Tag::createInstance( - $this->parsedContents[$i], - $this->docblock - ); - } - - //In order to allow "literal" inline tags, the otherwise invalid - //sequence "{@}" is changed to "@", and "{}" is changed to "}". - //See unit tests for examples. - for ($i=0; $i<$count; $i += 2) { - $this->parsedContents[$i] = str_replace( - array('{@}', '{}'), - array('@', '}'), - $this->parsedContents[$i] - ); - } - } - return $this->parsedContents; - } - - /** - * Return a formatted variant of the Long Description using MarkDown. - * - * @todo this should become a more intelligent piece of code where the - * configuration contains a setting what format long descriptions are. - * - * @codeCoverageIgnore Will be removed soon, in favor of adapters at - * PhpDocumentor itself that will process text in various formats. - * - * @return string - */ - public function getFormattedContents() - { - $result = $this->contents; - - // if the long description contains a plain HTML element, surround - // it with a pre element. Please note that we explicitly used str_replace - // and not preg_replace to gain performance - if (strpos($result, '') !== false) { - $result = str_replace( - array('', "\r\n", "\n", "\r", ''), - array('
', '', '', '', '
'), - $result - ); - } - - if (class_exists('Parsedown')) { - $markdown = \Parsedown::instance(); - $result = $markdown->parse($result); - } elseif (class_exists('dflydev\markdown\MarkdownExtraParser')) { - $markdown = new \dflydev\markdown\MarkdownExtraParser(); - $result = $markdown->transformMarkdown($result); - } elseif (class_exists('League\CommonMark\CommonMarkConverter')) { - $markdown = new \League\CommonMark\CommonMarkConverter(); - $result = $markdown->convertToHtml($result); - } - - return trim($result); - } - - /** - * Gets the docblock this tag belongs to. - * - * @return DocBlock The docblock this description belongs to. - */ - public function getDocBlock() - { - return $this->docblock; - } - - /** - * Sets the docblock this tag belongs to. - * - * @param DocBlock $docblock The new docblock this description belongs to. - * Setting NULL removes any association. - * - * @return $this - */ - public function setDocBlock(DocBlock $docblock = null) - { - $this->docblock = $docblock; - - return $this; - } - - /** - * Builds a string representation of this object. - * - * @todo determine the exact format as used by PHP Reflection - * and implement it. - * - * @return void - * @codeCoverageIgnore Not yet implemented - */ - public static function export() - { - throw new \Exception('Not yet implemented'); - } - - /** - * Returns the long description as a string. - * - * @return string - */ - public function __toString() - { - return $this->getContents(); - } -} diff --git a/src/phpDocumentor/Reflection/DocBlock/Location.php b/src/phpDocumentor/Reflection/DocBlock/Location.php deleted file mode 100644 index 6e5b33e..0000000 --- a/src/phpDocumentor/Reflection/DocBlock/Location.php +++ /dev/null @@ -1,82 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -/** - * The location a DocBlock occurs within a file. - * - * @author Vasil Rangelov - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class Location -{ - /** @var int Line where the DocBlock text starts. */ - protected $lineNumber = 0; - - /** @var int Column where the DocBlock text starts. */ - protected $columnNumber = 0; - - public function __construct($lineNumber = 0, $columnNumber = 0) - { - $this->setLineNumber($lineNumber)->setColumnNumber($columnNumber); - } - - /** - * Returns the line number that is covered by this location. - * - * @return integer - */ - public function getLineNumber() - { - return $this->lineNumber; - } - - /** - * Registers which line number is covered by this location object. - * - * @param integer $lineNumber - * - * @return $this - */ - public function setLineNumber($lineNumber) - { - $this->lineNumber = (int)$lineNumber; - - return $this; - } - - /** - * Returns the column number (character position on a line) for this location object. - * - * @return integer - */ - public function getColumnNumber() - { - return $this->columnNumber; - } - - /** - * Registers the column number (character position on a line) for this location object. - * - * @param integer $columnNumber - * - * @return $this - */ - public function setColumnNumber($columnNumber) - { - $this->columnNumber = (int)$columnNumber; - - return $this; - } -} diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag.php b/src/phpDocumentor/Reflection/DocBlock/Tag.php deleted file mode 100644 index 50f1b41..0000000 --- a/src/phpDocumentor/Reflection/DocBlock/Tag.php +++ /dev/null @@ -1,402 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -use phpDocumentor\Reflection\DocBlock; - -/** - * Parses a tag definition for a DocBlock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class Tag implements \Reflector -{ - /** - * PCRE regular expression matching a tag name. - */ - const REGEX_TAGNAME = '[\w\-\_\\\\]+'; - - /** @var string Name of the tag */ - protected $tag = ''; - - /** - * @var string|null Content of the tag. - * When set to NULL, it means it needs to be regenerated. - */ - protected $content = ''; - - /** @var string Description of the content of this tag */ - protected $description = ''; - - /** - * @var array|null The description, as an array of strings and Tag objects. - * When set to NULL, it means it needs to be regenerated. - */ - protected $parsedDescription = null; - - /** @var Location Location of the tag. */ - protected $location = null; - - /** @var DocBlock The DocBlock which this tag belongs to. */ - protected $docblock = null; - - /** - * @var array An array with a tag as a key, and an FQCN to a class that - * handles it as an array value. The class is expected to inherit this - * class. - */ - private static $tagHandlerMappings = array( - 'author' - => '\phpDocumentor\Reflection\DocBlock\Tag\AuthorTag', - 'covers' - => '\phpDocumentor\Reflection\DocBlock\Tag\CoversTag', - 'deprecated' - => '\phpDocumentor\Reflection\DocBlock\Tag\DeprecatedTag', - 'example' - => '\phpDocumentor\Reflection\DocBlock\Tag\ExampleTag', - 'link' - => '\phpDocumentor\Reflection\DocBlock\Tag\LinkTag', - 'method' - => '\phpDocumentor\Reflection\DocBlock\Tag\MethodTag', - 'param' - => '\phpDocumentor\Reflection\DocBlock\Tag\ParamTag', - 'property-read' - => '\phpDocumentor\Reflection\DocBlock\Tag\PropertyReadTag', - 'property' - => '\phpDocumentor\Reflection\DocBlock\Tag\PropertyTag', - 'property-write' - => '\phpDocumentor\Reflection\DocBlock\Tag\PropertyWriteTag', - 'return' - => '\phpDocumentor\Reflection\DocBlock\Tag\ReturnTag', - 'see' - => '\phpDocumentor\Reflection\DocBlock\Tag\SeeTag', - 'since' - => '\phpDocumentor\Reflection\DocBlock\Tag\SinceTag', - 'source' - => '\phpDocumentor\Reflection\DocBlock\Tag\SourceTag', - 'throw' - => '\phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag', - 'throws' - => '\phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag', - 'uses' - => '\phpDocumentor\Reflection\DocBlock\Tag\UsesTag', - 'var' - => '\phpDocumentor\Reflection\DocBlock\Tag\VarTag', - 'version' - => '\phpDocumentor\Reflection\DocBlock\Tag\VersionTag' - ); - - /** - * Factory method responsible for instantiating the correct sub type. - * - * @param string $tag_line The text for this tag, including description. - * @param DocBlock $docblock The DocBlock which this tag belongs to. - * @param Location $location Location of the tag. - * - * @throws \InvalidArgumentException if an invalid tag line was presented. - * - * @return static A new tag object. - */ - final public static function createInstance( - $tag_line, - DocBlock $docblock = null, - Location $location = null - ) { - $matches = self::extractTagParts($tag_line); - - $handler = __CLASS__; - if (isset(self::$tagHandlerMappings[$matches[1]])) { - $handler = self::$tagHandlerMappings[$matches[1]]; - } elseif (isset($docblock)) { - $tagName = (string)new Type\Collection( - array($matches[1]), - $docblock->getContext() - ); - - if (isset(self::$tagHandlerMappings[$tagName])) { - $handler = self::$tagHandlerMappings[$tagName]; - } - } - - return new $handler( - $matches[1], - isset($matches[2]) ? $matches[2] : '', - $docblock, - $location - ); - } - - /** - * Registers a handler for tags. - * - * Registers a handler for tags. The class specified is autoloaded if it's - * not available. It must inherit from this class. - * - * @param string $tag Name of tag to regiser a handler for. When - * registering a namespaced tag, the full name, along with a prefixing - * slash MUST be provided. - * @param string|null $handler FQCN of handler. Specifing NULL removes the - * handler for the specified tag, if any. - * - * @return bool TRUE on success, FALSE on failure. - */ - final public static function registerTagHandler($tag, $handler) - { - $tag = trim((string)$tag); - - if (null === $handler) { - unset(self::$tagHandlerMappings[$tag]); - return true; - } - - if ('' !== $tag - && class_exists($handler, true) - && is_subclass_of($handler, __CLASS__) - && !strpos($tag, '\\') //Accept no slash, and 1st slash at offset 0. - ) { - self::$tagHandlerMappings[$tag] = $handler; - return true; - } - - return false; - } - - /** - * Parses a tag and populates the member variables. - * - * @param string $name Name of the tag. - * @param string $content The contents of the given tag. - * @param DocBlock $docblock The DocBlock which this tag belongs to. - * @param Location $location Location of the tag. - */ - public function __construct( - $name, - $content, - DocBlock $docblock = null, - Location $location = null - ) { - $this - ->setName($name) - ->setContent($content) - ->setDocBlock($docblock) - ->setLocation($location); - } - - /** - * Gets the name of this tag. - * - * @return string The name of this tag. - */ - public function getName() - { - return $this->tag; - } - - /** - * Sets the name of this tag. - * - * @param string $name The new name of this tag. - * - * @throws \InvalidArgumentException When an invalid tag name is provided. - * - * @return $this - */ - public function setName($name) - { - $this->validateTagName($name); - - $this->tag = $name; - - return $this; - } - - /** - * Gets the content of this tag. - * - * @return string - */ - public function getContent() - { - if (null === $this->content) { - $this->content = $this->description; - } - - return $this->content; - } - - /** - * Sets the content of this tag. - * - * @param string $content The new content of this tag. - * - * @return $this - */ - public function setContent($content) - { - $this->setDescription($content); - $this->content = $content; - - return $this; - } - - /** - * Gets the description component of this tag. - * - * @return string - */ - public function getDescription() - { - return $this->description; - } - - /** - * Sets the description component of this tag. - * - * @param string $description The new description component of this tag. - * - * @return $this - */ - public function setDescription($description) - { - $this->content = null; - $this->parsedDescription = null; - $this->description = trim($description); - - return $this; - } - - /** - * Gets the parsed text of this description. - * - * @return array An array of strings and tag objects, in the order they - * occur within the description. - */ - public function getParsedDescription() - { - if (null === $this->parsedDescription) { - $description = new Description($this->description, $this->docblock); - $this->parsedDescription = $description->getParsedContents(); - } - return $this->parsedDescription; - } - - /** - * Gets the docblock this tag belongs to. - * - * @return DocBlock The docblock this tag belongs to. - */ - public function getDocBlock() - { - return $this->docblock; - } - - /** - * Sets the docblock this tag belongs to. - * - * @param DocBlock $docblock The new docblock this tag belongs to. Setting - * NULL removes any association. - * - * @return $this - */ - public function setDocBlock(DocBlock $docblock = null) - { - $this->docblock = $docblock; - - return $this; - } - - /** - * Gets the location of the tag. - * - * @return Location The tag's location. - */ - public function getLocation() - { - return $this->location; - } - - /** - * Sets the location of the tag. - * - * @param Location $location The new location of the tag. - * - * @return $this - */ - public function setLocation(Location $location = null) - { - $this->location = $location; - - return $this; - } - - /** - * Builds a string representation of this object. - * - * @todo determine the exact format as used by PHP Reflection and implement it. - * - * @return void - * @codeCoverageIgnore Not yet implemented - */ - public static function export() - { - throw new \Exception('Not yet implemented'); - } - - /** - * Returns the tag as a serialized string - * - * @return string - */ - public function __toString() - { - return "@{$this->getName()} {$this->getContent()}"; - } - - /** - * Extracts all components for a tag. - * - * @param string $tagLine - * - * @return string[] - */ - private static function extractTagParts($tagLine) - { - $matches = array(); - if (! preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)?/us', $tagLine, $matches)) { - throw new \InvalidArgumentException( - 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' - ); - } - - return $matches; - } - - /** - * Validates if the tag name matches the expected format, otherwise throws an exception. - * - * @param string $name - * - * @return void - */ - private function validateTagName($name) - { - if (!preg_match('/^' . self::REGEX_TAGNAME . '$/u', $name)) { - throw new \InvalidArgumentException( - 'The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, ' - . 'hyphens and backslashes.' - ); - } - } -} diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/AuthorTag.php b/src/phpDocumentor/Reflection/DocBlock/Tag/AuthorTag.php deleted file mode 100644 index bacf52e..0000000 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/AuthorTag.php +++ /dev/null @@ -1,131 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tag; - -use phpDocumentor\Reflection\DocBlock\Tag; - -/** - * Reflection class for an @author tag in a Docblock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class AuthorTag extends Tag -{ - /** - * PCRE regular expression matching any valid value for the name component. - */ - const REGEX_AUTHOR_NAME = '[^\<]*'; - - /** - * PCRE regular expression matching any valid value for the email component. - */ - const REGEX_AUTHOR_EMAIL = '[^\>]*'; - - /** @var string The name of the author */ - protected $authorName = ''; - - /** @var string The email of the author */ - protected $authorEmail = ''; - - public function getContent() - { - if (null === $this->content) { - $this->content = $this->authorName; - if ('' != $this->authorEmail) { - $this->content .= "<{$this->authorEmail}>"; - } - } - - return $this->content; - } - - /** - * {@inheritdoc} - */ - public function setContent($content) - { - parent::setContent($content); - if (preg_match( - '/^(' . self::REGEX_AUTHOR_NAME . - ')(\<(' . self::REGEX_AUTHOR_EMAIL . - ')\>)?$/u', - $this->description, - $matches - )) { - $this->authorName = trim($matches[1]); - if (isset($matches[3])) { - $this->authorEmail = trim($matches[3]); - } - } - - return $this; - } - - /** - * Gets the author's name. - * - * @return string The author's name. - */ - public function getAuthorName() - { - return $this->authorName; - } - - /** - * Sets the author's name. - * - * @param string $authorName The new author name. - * An invalid value will set an empty string. - * - * @return $this - */ - public function setAuthorName($authorName) - { - $this->content = null; - $this->authorName - = preg_match('/^' . self::REGEX_AUTHOR_NAME . '$/u', $authorName) - ? $authorName : ''; - - return $this; - } - - /** - * Gets the author's email. - * - * @return string The author's email. - */ - public function getAuthorEmail() - { - return $this->authorEmail; - } - - /** - * Sets the author's email. - * - * @param string $authorEmail The new author email. - * An invalid value will set an empty string. - * - * @return $this - */ - public function setAuthorEmail($authorEmail) - { - $this->authorEmail - = preg_match('/^' . self::REGEX_AUTHOR_EMAIL . '$/u', $authorEmail) - ? $authorEmail : ''; - - $this->content = null; - return $this; - } -} diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/CoversTag.php b/src/phpDocumentor/Reflection/DocBlock/Tag/CoversTag.php deleted file mode 100644 index bd31b56..0000000 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/CoversTag.php +++ /dev/null @@ -1,24 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tag; - -/** - * Reflection class for a @covers tag in a Docblock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class CoversTag extends SeeTag -{ -} diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTag.php b/src/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTag.php deleted file mode 100644 index 7226316..0000000 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTag.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tag; - -use phpDocumentor\Reflection\DocBlock\Tag\VersionTag; - -/** - * Reflection class for a @deprecated tag in a Docblock. - * - * @author Vasil Rangelov - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class DeprecatedTag extends VersionTag -{ -} diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/SeeTag.php b/src/phpDocumentor/Reflection/DocBlock/Tag/SeeTag.php deleted file mode 100644 index 4f5f22c..0000000 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/SeeTag.php +++ /dev/null @@ -1,81 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tag; - -use phpDocumentor\Reflection\DocBlock\Tag; - -/** - * Reflection class for a @see tag in a Docblock. - * - * @author Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class SeeTag extends Tag -{ - /** @var string */ - protected $refers = null; - - /** - * {@inheritdoc} - */ - public function getContent() - { - if (null === $this->content) { - $this->content = "{$this->refers} {$this->description}"; - } - return $this->content; - } - - /** - * {@inheritdoc} - */ - public function setContent($content) - { - parent::setContent($content); - $parts = preg_split('/\s+/Su', $this->description, 2); - - // any output is considered a type - $this->refers = $parts[0]; - - $this->setDescription(isset($parts[1]) ? $parts[1] : ''); - - $this->content = $content; - return $this; - } - - /** - * Gets the structural element this tag refers to. - * - * @return string - */ - public function getReference() - { - return $this->refers; - } - - /** - * Sets the structural element this tag refers to. - * - * @param string $refers The new type this tag refers to. - * - * @return $this - */ - public function setReference($refers) - { - $this->refers = $refers; - - $this->content = null; - return $this; - } -} diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag/VersionTag.php b/src/phpDocumentor/Reflection/DocBlock/Tag/VersionTag.php deleted file mode 100644 index 260f698..0000000 --- a/src/phpDocumentor/Reflection/DocBlock/Tag/VersionTag.php +++ /dev/null @@ -1,108 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tag; - -use phpDocumentor\Reflection\DocBlock\Tag; - -/** - * Reflection class for a @version tag in a Docblock. - * - * @author Vasil Rangelov - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class VersionTag extends Tag -{ - /** - * PCRE regular expression matching a version vector. - * Assumes the "x" modifier. - */ - const REGEX_VECTOR = '(?: - # Normal release vectors. - \d\S* - | - # VCS version vectors. Per PHPCS, they are expected to - # follow the form of the VCS name, followed by ":", followed - # by the version vector itself. - # By convention, popular VCSes like CVS, SVN and GIT use "$" - # around the actual version vector. - [^\s\:]+\:\s*\$[^\$]+\$ - )'; - - /** @var string The version vector. */ - protected $version = ''; - - public function getContent() - { - if (null === $this->content) { - $this->content = "{$this->version} {$this->description}"; - } - - return $this->content; - } - - /** - * {@inheritdoc} - */ - public function setContent($content) - { - parent::setContent($content); - - if (preg_match( - '/^ - # The version vector - (' . self::REGEX_VECTOR . ') - \s* - # The description - (.+)? - $/sux', - $this->description, - $matches - )) { - $this->version = $matches[1]; - $this->setDescription(isset($matches[2]) ? $matches[2] : ''); - $this->content = $content; - } - - return $this; - } - - /** - * Gets the version section of the tag. - * - * @return string The version section of the tag. - */ - public function getVersion() - { - return $this->version; - } - - /** - * Sets the version section of the tag. - * - * @param string $version The new version section of the tag. - * An invalid value will set an empty string. - * - * @return $this - */ - public function setVersion($version) - { - $this->version - = preg_match('/^' . self::REGEX_VECTOR . '$/ux', $version) - ? $version - : ''; - - $this->content = null; - return $this; - } -} diff --git a/tests/phpDocumentor/Reflection/DocBlock/ContextFactoryTest.php b/tests/phpDocumentor/Reflection/DocBlock/ContextFactoryTest.php deleted file mode 100644 index 418e40d..0000000 --- a/tests/phpDocumentor/Reflection/DocBlock/ContextFactoryTest.php +++ /dev/null @@ -1,94 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock { - -// Added imports on purpose as mock for the unit tests, please do not remove. - use Mockery as m; - use phpDocumentor\Reflection\DocBlock, - phpDocumentor\Reflection\DocBlock\Tag; - use \ReflectionClass; // yes, the slash is part of the test - - /** - * @coversDefaultClass \phpDocumentor\Reflection\DocBlock\ContextFactory - * @covers :: - */ - class ContextFactoryTest extends \PHPUnit_Framework_TestCase - { - /** - * @covers ::createFromClassReflector - * @covers ::createForNamespace - * @uses phpDocumentor\Reflection\DocBlock\Context - */ - public function testReadsNamespaceFromClassReflection() - { - $fixture = new ContextFactory(); - $context = $fixture->createFromClassReflector(new ReflectionClass($this)); - - $this->assertSame(__NAMESPACE__, $context->getNamespace()); - } - - /** - * @covers ::createFromClassReflector - * @covers ::createForNamespace - * @uses phpDocumentor\Reflection\DocBlock\Context - */ - public function testReadsAliasesFromClassReflection() - { - $fixture = new ContextFactory(); - $expected = [ - 'm' => 'Mockery', - 'DocBlock' => 'phpDocumentor\Reflection\DocBlock', - 'Tag' => 'phpDocumentor\Reflection\DocBlock\Tag', - 'ReflectionClass' => 'ReflectionClass' - ]; - $context = $fixture->createFromClassReflector(new ReflectionClass($this)); - - $this->assertSame($expected, $context->getNamespaceAliases()); - } - - /** - * @covers ::createForNamespace - * @uses phpDocumentor\Reflection\DocBlock\Context - */ - public function testReadsNamespaceFromProvidedNamespaceAndContent() - { - $fixture = new ContextFactory(); - $context = $fixture->createForNamespace(__NAMESPACE__, file_get_contents(__FILE__)); - - $this->assertSame(__NAMESPACE__, $context->getNamespace()); - } - - /** - * @covers ::createForNamespace - * @uses phpDocumentor\Reflection\DocBlock\Context - */ - public function testReadsAliasesFromProvidedNamespaceAndContent() - { - $fixture = new ContextFactory(); - $expected = [ - 'm' => 'Mockery', - 'DocBlock' => 'phpDocumentor\Reflection\DocBlock', - 'Tag' => 'phpDocumentor\Reflection\DocBlock\Tag', - 'ReflectionClass' => 'ReflectionClass' - ]; - $context = $fixture->createForNamespace(__NAMESPACE__, file_get_contents(__FILE__)); - - $this->assertSame($expected, $context->getNamespaceAliases()); - } - } -} - -namespace phpDocumentor\Reflection\DocBlock\Mock { - // the following import should not show in the tests above - use phpDocumentor\Reflection\DocBlock\Description; -} diff --git a/tests/phpDocumentor/Reflection/DocBlock/ContextTest.php b/tests/phpDocumentor/Reflection/DocBlock/ContextTest.php deleted file mode 100644 index 8ce8701..0000000 --- a/tests/phpDocumentor/Reflection/DocBlock/ContextTest.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -use Mockery as m; - -/** - * @coversDefaultClass \phpDocumentor\Reflection\DocBlock\Context - */ -class ContextTest extends \PHPUnit_Framework_TestCase -{ - /** - * @covers ::__construct - * @covers ::getNamespace - */ - public function testProvidesANormalizedNamespace() - { - $fixture = new Context('\My\Space'); - $this->assertSame('My\Space', $fixture->getNamespace()); - } - - /** - * @covers ::__construct - * @covers ::getNamespace - */ - public function testInterpretsNamespaceNamedGlobalAsRootNamespace() - { - $fixture = new Context('global'); - $this->assertSame('', $fixture->getNamespace()); - } - - /** - * @covers ::__construct - * @covers ::getNamespace - */ - public function testInterpretsNamespaceNamedDefaultAsRootNamespace() - { - $fixture = new Context('default'); - $this->assertSame('', $fixture->getNamespace()); - } - - /** - * @covers ::__construct - * @covers ::getNamespaceAliases - */ - public function testProvidesNormalizedNamespaceAliases() - { - $fixture = new Context('', ['Space' => '\My\Space']); - $this->assertSame(['Space' => 'My\Space'], $fixture->getNamespaceAliases()); - } -} diff --git a/tests/phpDocumentor/Reflection/DocBlock/DescriptionTest.php b/tests/phpDocumentor/Reflection/DocBlock/DescriptionTest.php deleted file mode 100644 index a6ca7b3..0000000 --- a/tests/phpDocumentor/Reflection/DocBlock/DescriptionTest.php +++ /dev/null @@ -1,245 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -/** - * Test class for \phpDocumentor\Reflection\DocBlock\Description - * - * @author Vasil Rangelov - * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class DescriptionTest extends \PHPUnit_Framework_TestCase -{ - public function testConstruct() - { - $fixture = <<assertSame($fixture, $object->getContents()); - - $parsedContents = $object->getParsedContents(); - $this->assertCount(1, $parsedContents); - $this->assertSame($fixture, $parsedContents[0]); - } - - public function testInlineTagParsing() - { - $fixture = <<assertSame($fixture, $object->getContents()); - - $parsedContents = $object->getParsedContents(); - $this->assertCount(3, $parsedContents); - $this->assertSame('This is text for a ', $parsedContents[0]); - $this->assertInstanceOf( - __NAMESPACE__ . '\Tag\LinkTag', - $parsedContents[1] - ); - $this->assertSame( - ' that uses inline -tags.', - $parsedContents[2] - ); - } - - public function testInlineTagAtStartParsing() - { - $fixture = <<assertSame($fixture, $object->getContents()); - - $parsedContents = $object->getParsedContents(); - $this->assertCount(3, $parsedContents); - - $this->assertSame('', $parsedContents[0]); - $this->assertInstanceOf( - __NAMESPACE__ . '\Tag\LinkTag', - $parsedContents[1] - ); - $this->assertSame( - ' is text for a description that uses inline -tags.', - $parsedContents[2] - ); - } - - public function testNestedInlineTagParsing() - { - $fixture = <<assertSame($fixture, $object->getContents()); - - $parsedContents = $object->getParsedContents(); - $this->assertCount(3, $parsedContents); - - $this->assertSame( - 'This is text for a description with ', - $parsedContents[0] - ); - $this->assertInstanceOf( - __NAMESPACE__ . '\Tag', - $parsedContents[1] - ); - $this->assertSame('.', $parsedContents[2]); - - $parsedDescription = $parsedContents[1]->getParsedDescription(); - $this->assertCount(3, $parsedDescription); - $this->assertSame("inline tag with\n", $parsedDescription[0]); - $this->assertInstanceOf( - __NAMESPACE__ . '\Tag\LinkTag', - $parsedDescription[1] - ); - $this->assertSame(' in it', $parsedDescription[2]); - } - - public function testLiteralOpeningDelimiter() - { - $fixture = <<assertSame($fixture, $object->getContents()); - - $parsedContents = $object->getParsedContents(); - $this->assertCount(1, $parsedContents); - $this->assertSame($fixture, $parsedContents[0]); - } - - public function testNestedLiteralOpeningDelimiter() - { - $fixture = <<assertSame($fixture, $object->getContents()); - - $parsedContents = $object->getParsedContents(); - $this->assertCount(3, $parsedContents); - $this->assertSame( - 'This is text for a description containing ', - $parsedContents[0] - ); - $this->assertInstanceOf( - __NAMESPACE__ . '\Tag', - $parsedContents[1] - ); - $this->assertSame('.', $parsedContents[2]); - - $this->assertSame( - array('inline tag that has { that -is literal'), - $parsedContents[1]->getParsedDescription() - ); - } - - public function testLiteralClosingDelimiter() - { - $fixture = <<assertSame($fixture, $object->getContents()); - - $parsedContents = $object->getParsedContents(); - $this->assertCount(1, $parsedContents); - $this->assertSame( - 'This is text for a description with } that is not a tag.', - $parsedContents[0] - ); - } - - public function testNestedLiteralClosingDelimiter() - { - $fixture = <<assertSame($fixture, $object->getContents()); - - $parsedContents = $object->getParsedContents(); - $this->assertCount(3, $parsedContents); - $this->assertSame( - 'This is text for a description with ', - $parsedContents[0] - ); - $this->assertInstanceOf( - __NAMESPACE__ . '\Tag', - $parsedContents[1] - ); - $this->assertSame('.', $parsedContents[2]); - - $this->assertSame( - array('inline tag with } that is not an -inline tag'), - $parsedContents[1]->getParsedDescription() - ); - } - - public function testInlineTagEscapingSequence() - { - $fixture = <<assertSame($fixture, $object->getContents()); - - $parsedContents = $object->getParsedContents(); - $this->assertCount(1, $parsedContents); - $this->assertSame( - 'This is text for a description with literal {@link}.', - $parsedContents[0] - ); - } - - public function testNestedInlineTagEscapingSequence() - { - $fixture = <<assertSame($fixture, $object->getContents()); - - $parsedContents = $object->getParsedContents(); - $this->assertCount(3, $parsedContents); - $this->assertSame( - 'This is text for a description with an ', - $parsedContents[0] - ); - $this->assertInstanceOf( - __NAMESPACE__ . '\Tag', - $parsedContents[1] - ); - $this->assertSame('.', $parsedContents[2]); - - $this->assertSame( - array('inline tag with literal -{@link} in it'), - $parsedContents[1]->getParsedDescription() - ); - } -} diff --git a/tests/phpDocumentor/Reflection/DocBlock/Type/CollectionTest.php b/tests/phpDocumentor/Reflection/DocBlock/Type/CollectionTest.php deleted file mode 100644 index 78c7306..0000000 --- a/tests/phpDocumentor/Reflection/DocBlock/Type/CollectionTest.php +++ /dev/null @@ -1,195 +0,0 @@ - - * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Type; - -use phpDocumentor\Reflection\DocBlock\Context; - -/** - * Test class for \phpDocumentor\Reflection\DocBlock\Type\Collection - * - * @covers phpDocumentor\Reflection\DocBlock\Type\Collection - * - * @author Mike van Riel - * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ -class CollectionTest extends \PHPUnit_Framework_TestCase -{ - /** - * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::__construct - * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::getContext - * - * @return void - */ - public function testConstruct() - { - $collection = new Collection(); - $this->assertCount(0, $collection); - $this->assertEquals('', $collection->getContext()->getNamespace()); - $this->assertCount(0, $collection->getContext()->getNamespaceAliases()); - } - - /** - * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::__construct - * - * @return void - */ - public function testConstructWithTypes() - { - $collection = new Collection(array('integer', 'string')); - $this->assertCount(2, $collection); - } - - /** - * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::__construct - * - * @return void - */ - public function testConstructWithNamespace() - { - $collection = new Collection(array(), new Context('\My\Space')); - $this->assertEquals('My\Space', $collection->getContext()->getNamespace()); - - $collection = new Collection(array(), new Context('My\Space')); - $this->assertEquals('My\Space', $collection->getContext()->getNamespace()); - - $collection = new Collection(array(), null); - $this->assertEquals('', $collection->getContext()->getNamespace()); - } - - /** - * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::__construct - * - * @return void - */ - public function testConstructWithNamespaceAliases() - { - $fixture = array('a' => 'b'); - $collection = new Collection(array(), new Context(null, $fixture)); - $this->assertEquals( - array('a' => '\b'), - $collection->getContext()->getNamespaceAliases() - ); - } - - /** - * @param string $fixture - * @param array $expected - * - * @dataProvider provideTypesToExpand - * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::add - * - * @return void - */ - public function testAdd($fixture, $expected) - { - $collection = new Collection( - array(), - new Context('\My\Space', array('Alias' => '\My\Space\Aliasing')) - ); - $collection->add($fixture); - - $this->assertSame($expected, $collection->getArrayCopy()); - } - - /** - * @param string $fixture - * @param array $expected - * - * @dataProvider provideTypesToExpandWithoutNamespace - * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::add - * - * @return void - */ - public function testAddWithoutNamespace($fixture, $expected) - { - $collection = new Collection( - array(), - new Context(null, array('Alias' => '\My\Space\Aliasing')) - ); - $collection->add($fixture); - - $this->assertSame($expected, $collection->getArrayCopy()); - } - - /** - * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::add - * @expectedException InvalidArgumentException - * - * @return void - */ - public function testAddWithInvalidArgument() - { - $collection = new Collection(); - $collection->add(array()); - } - - /** - * Returns the types and their expected values to test the retrieval of - * types. - * - * @param string $method Name of the method consuming this data provider. - * @param string $namespace Name of the namespace to user as basis. - * - * @return string[] - */ - public function provideTypesToExpand($method, $namespace = '\My\Space\\') - { - return array( - array('', array()), - array(' ', array()), - array('int', array('int')), - array('int ', array('int')), - array('string', array('string')), - array('DocBlock', array($namespace.'DocBlock')), - array('DocBlock[]', array($namespace.'DocBlock[]')), - array(' DocBlock ', array($namespace.'DocBlock')), - array('\My\Space\DocBlock', array('\My\Space\DocBlock')), - array('Alias\DocBlock', array('\My\Space\Aliasing\DocBlock')), - array( - 'DocBlock|Tag', - array($namespace .'DocBlock', $namespace .'Tag') - ), - array( - 'DocBlock|null', - array($namespace.'DocBlock', 'null') - ), - array( - '\My\Space\DocBlock|Tag', - array('\My\Space\DocBlock', $namespace.'Tag') - ), - array( - 'DocBlock[]|null', - array($namespace.'DocBlock[]', 'null') - ), - array( - 'DocBlock[]|int[]', - array($namespace.'DocBlock[]', 'int[]') - ), - ); - } - - /** - * Returns the types and their expected values to test the retrieval of - * types when no namespace is available. - * - * @param string $method Name of the method consuming this data provider. - * - * @return string[] - */ - public function provideTypesToExpandWithoutNamespace($method) - { - return $this->provideTypesToExpand($method, '\\'); - } -} diff --git a/tests/phpDocumentor/Reflection/Types/ResolverTest.php b/tests/phpDocumentor/Reflection/Types/ResolverTest.php deleted file mode 100644 index 6e90643..0000000 --- a/tests/phpDocumentor/Reflection/Types/ResolverTest.php +++ /dev/null @@ -1,348 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use Mockery as m; -use phpDocumentor\Reflection\DocBlock\Context; -use phpDocumentor\Reflection\Type; - -/** - * @coversDefaultClass phpDocumentor\Reflection\Types\Resolver - */ -class ResolverTest extends \PHPUnit_Framework_TestCase -{ - /** - * @param string $keyword - * @param string $expectedClass - * - * @covers ::resolve - * @covers :: - * - * @uses phpDocumentor\Reflection\DocBlock\Context - * @uses phpDocumentor\Reflection\Types\Array_ - * @uses phpDocumentor\Reflection\Types\Object_ - * - * @dataProvider provideKeywords - */ - public function testResolvingKeywords($keyword, $expectedClass) - { - $fixture = new Resolver(); - - $resolvedType = $fixture->resolve($keyword, new Context('')); - - $this->assertInstanceOf($expectedClass, $resolvedType); - } - - /** - * @param string $fqsen - * - * @covers ::resolve - * @covers :: - * - * @uses phpDocumentor\Reflection\DocBlock\Context - * @uses phpDocumentor\Reflection\Types\Object_ - * @uses phpDocumentor\Reflection\Fqsen - * - * @dataProvider provideFqsen - */ - public function testResolvingFQSENs($fqsen) - { - $fixture = new Resolver(); - - /** @var Object_ $resolvedType */ - $resolvedType = $fixture->resolve($fqsen, new Context('')); - - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Object_', $resolvedType); - $this->assertInstanceOf('phpDocumentor\Reflection\Fqsen', $resolvedType->getFqsen()); - $this->assertSame($fqsen, (string)$resolvedType); - } - - - /** - * @covers ::resolve - * @covers :: - * - * @uses phpDocumentor\Reflection\DocBlock\Context - * @uses phpDocumentor\Reflection\Types\Object_ - * @uses phpDocumentor\Reflection\Fqsen - */ - public function testResolvingRelativeQSENsBasedOnNamespace() - { - $fixture = new Resolver(); - - /** @var Object_ $resolvedType */ - $resolvedType = $fixture->resolve('DocBlock', new Context('phpDocumentor\Reflection')); - - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Object_', $resolvedType); - $this->assertInstanceOf('phpDocumentor\Reflection\Fqsen', $resolvedType->getFqsen()); - $this->assertSame('\phpDocumentor\Reflection\DocBlock', (string)$resolvedType); - } - - /** - * @covers ::resolve - * @covers :: - * - * @uses phpDocumentor\Reflection\DocBlock\Context - * @uses phpDocumentor\Reflection\Types\Object_ - * @uses phpDocumentor\Reflection\Fqsen - */ - public function testResolvingRelativeQSENsBasedOnNamespaceAlias() - { - $fixture = new Resolver(); - - /** @var Object_ $resolvedType */ - $resolvedType = $fixture->resolve( - 'm\MockInterface', - new Context('phpDocumentor\Reflection', ['m' => '\Mockery']) - ); - - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Object_', $resolvedType); - $this->assertInstanceOf('phpDocumentor\Reflection\Fqsen', $resolvedType->getFqsen()); - $this->assertSame('\Mockery\MockInterface', (string)$resolvedType); - } - - /** - * @covers ::resolve - * @covers :: - * - * @uses phpDocumentor\Reflection\DocBlock\Context - * @uses phpDocumentor\Reflection\Types\Array_ - * @uses phpDocumentor\Reflection\Types\String - */ - public function testResolvingTypedArrays() - { - $fixture = new Resolver(); - - /** @var Array_ $resolvedType */ - $resolvedType = $fixture->resolve('string[]', new Context('')); - - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $resolvedType); - $this->assertSame('string[]', (string)$resolvedType); - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Mixed', $resolvedType->getKeyType()); - $this->assertInstanceOf('phpDocumentor\Reflection\Types\String', $resolvedType->getValueType()); - } - - /** - * @covers ::resolve - * @covers :: - * - * @uses phpDocumentor\Reflection\DocBlock\Context - * @uses phpDocumentor\Reflection\Types\Array_ - * @uses phpDocumentor\Reflection\Types\String - */ - public function testResolvingNestedTypedArrays() - { - $fixture = new Resolver(); - - /** @var Array_ $resolvedType */ - $resolvedType = $fixture->resolve('string[][]', new Context('')); - - /** @var Array_ $childValueType */ - $childValueType = $resolvedType->getValueType(); - - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $resolvedType); - - $this->assertSame('string[][]', (string)$resolvedType); - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Mixed', $resolvedType->getKeyType()); - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $childValueType); - - $this->assertSame('string[]', (string)$childValueType); - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Mixed', $childValueType->getKeyType()); - $this->assertInstanceOf('phpDocumentor\Reflection\Types\String', $childValueType->getValueType()); - } - - /** - * @covers ::resolve - * @covers :: - * - * @uses phpDocumentor\Reflection\DocBlock\Context - * @uses phpDocumentor\Reflection\Types\Compound - * @uses phpDocumentor\Reflection\Types\String - * @uses phpDocumentor\Reflection\Types\Object_ - * @uses phpDocumentor\Reflection\Fqsen - */ - public function testResolvingCompoundTypes() - { - $fixture = new Resolver(); - - /** @var Compound $resolvedType */ - $resolvedType = $fixture->resolve('string|Reflection\DocBlock', new Context('phpDocumentor')); - - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Compound', $resolvedType); - $this->assertSame('string|\phpDocumentor\Reflection\DocBlock', (string)$resolvedType); - - /** @var String $secondType */ - $firstType = $resolvedType->get(0); - - /** @var Object_ $secondType */ - $secondType = $resolvedType->get(1); - - $this->assertInstanceOf('phpDocumentor\Reflection\Types\String', $firstType); - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Object_', $secondType); - $this->assertInstanceOf('phpDocumentor\Reflection\Fqsen', $secondType->getFqsen()); - } - - /** - * This test asserts that the parameter order is correct. - * - * When you pass two arrays separated by the compound operator (i.e. 'integer[]|string[]') then we always split the - * expression in its compound parts and then we parse the types with the array operators. If we were to switch the - * order around then 'integer[]|string[]' would read as an array of string or integer array; which is something - * other than what we intend. - * - * @covers ::resolve - * @covers :: - * - * @uses phpDocumentor\Reflection\DocBlock\Context - * @uses phpDocumentor\Reflection\Types\Compound - * @uses phpDocumentor\Reflection\Types\Array_ - * @uses phpDocumentor\Reflection\Types\Integer - * @uses phpDocumentor\Reflection\Types\String - */ - public function testResolvingCompoundTypesWithTwoArrays() - { - $fixture = new Resolver(); - - /** @var Compound $resolvedType */ - $resolvedType = $fixture->resolve('integer[]|string[]', new Context('')); - - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Compound', $resolvedType); - $this->assertSame('int[]|string[]', (string)$resolvedType); - - /** @var Array_ $firstType */ - $firstType = $resolvedType->get(0); - - /** @var Array_ $secondType */ - $secondType = $resolvedType->get(1); - - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $firstType); - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Integer', $firstType->getValueType()); - $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $secondType); - $this->assertInstanceOf('phpDocumentor\Reflection\Types\String', $secondType->getValueType()); - } - - /** - * @covers ::addKeyword - * @uses phpDocumentor\Reflection\Types\Resolver::resolve - * @uses phpDocumentor\Reflection\Types\Resolver:: - * @uses phpDocumentor\Reflection\DocBlock\Context - */ - public function testAddingAKeyword() - { - // Assign - $typeMock = m::mock(Type::class); - - // Act - $fixture = new Resolver(); - $fixture->addKeyword('mock', get_class($typeMock)); - - // Assert - $result = $fixture->resolve('mock', new Context('')); - $this->assertInstanceOf(get_class($typeMock), $result); - $this->assertNotSame($typeMock, $result); - } - - /** - * @covers ::addKeyword - * @uses phpDocumentor\Reflection\DocBlock\Context - * @expectedException \InvalidArgumentException - */ - public function testAddingAKeywordFailsIfTypeClassDoesNotExist() - { - $fixture = new Resolver(); - $fixture->addKeyword('mock', 'IDoNotExist'); - } - - /** - * @covers ::addKeyword - * @uses phpDocumentor\Reflection\DocBlock\Context - * @expectedException \InvalidArgumentException - */ - public function testAddingAKeywordFailsIfTypeClassDoesNotImplementTypeInterface() - { - $fixture = new Resolver(); - $fixture->addKeyword('mock', 'stdClass'); - } - - /** - * @covers ::resolve - * @uses phpDocumentor\Reflection\DocBlock\Context - * - * @expectedException \InvalidArgumentException - */ - public function testExceptionIsThrownIfTypeIsEmpty() - { - $fixture = new Resolver(); - $fixture->resolve(' ', new Context('')); - } - - /** - * @covers ::resolve - * @uses phpDocumentor\Reflection\DocBlock\Context - * - * @expectedException \InvalidArgumentException - */ - public function testExceptionIsThrownIfTypeIsNotAString() - { - $fixture = new Resolver(); - $fixture->resolve(['a'], new Context('')); - } - - /** - * Returns a list of keywords and expected classes that are created from them. - * - * @return string[][] - */ - public function provideKeywords() - { - return [ - ['string', 'phpDocumentor\Reflection\Types\String'], - ['int', 'phpDocumentor\Reflection\Types\Integer'], - ['integer', 'phpDocumentor\Reflection\Types\Integer'], - ['float', 'phpDocumentor\Reflection\Types\Float'], - ['double', 'phpDocumentor\Reflection\Types\Float'], - ['bool', 'phpDocumentor\Reflection\Types\Boolean'], - ['boolean', 'phpDocumentor\Reflection\Types\Boolean'], - ['resource', 'phpDocumentor\Reflection\Types\Resource'], - ['null', 'phpDocumentor\Reflection\Types\Null_'], - ['callable', 'phpDocumentor\Reflection\Types\Callable_'], - ['callback', 'phpDocumentor\Reflection\Types\Callable_'], - ['array', 'phpDocumentor\Reflection\Types\Array_'], - ['scalar', 'phpDocumentor\Reflection\Types\Scalar'], - ['object', 'phpDocumentor\Reflection\Types\Object_'], - ['mixed', 'phpDocumentor\Reflection\Types\Mixed'], - ['void', 'phpDocumentor\Reflection\Types\Void'], - ['$this', 'phpDocumentor\Reflection\Types\This'], - ['static', 'phpDocumentor\Reflection\Types\Static_'], - ['self', 'phpDocumentor\Reflection\Types\Self_'], - ]; - } - - /** - * Provides a list of FQSENs to test the resolution patterns with. - * - * @return string[][] - */ - public function provideFqsen() - { - return [ - 'namespace' => ['\phpDocumentor\Reflection'], - 'class' => ['\phpDocumentor\Reflection\DocBlock'], - 'function' => ['\DI\object()'], - 'constant' => ['\phpDocumentor\Reflection\GLOBAL_CONSTANT'], - 'classConstant' => ['\phpDocumentor\Reflection\DocBlock::CONSTANT'], - 'property' => ['\phpDocumentor\Reflection\DocBlock::$summary'], - 'method' => ['\phpDocumentor\Reflection\DocBlock::getSummary()'], - ]; - } -} diff --git a/tests/unit/DocBlock/DescriptionTest.php b/tests/unit/DocBlock/DescriptionTest.php new file mode 100644 index 0000000..44794fb --- /dev/null +++ b/tests/unit/DocBlock/DescriptionTest.php @@ -0,0 +1,109 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use Mockery as m; +use phpDocumentor\Reflection\DocBlock\Tags\Deprecated; +use phpDocumentor\Reflection\DocBlock\Tags\Link; + +/** + * @coversDefaultClass \phpDocumentor\Reflection\DocBlock\Description + */ +class DescriptionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @param array $examples + * @dataProvider provideExampleDescriptions + * @covers ::__construct + * @covers ::render + * @covers ::parse + * @uses phpDocumentor\Reflection\DocBlock\Description\PassthroughFormatter + * @uses phpDocumentor\Reflection\DocBlock\Tag + * @uses phpDocumentor\Reflection\DocBlock\Tags\Link + */ + public function testParsesDescription($example) + { + $object = new Description($example); + + $this->assertSame($example, $object->render()); + } + + /** + * @covers ::__construct + * @covers ::render + * @covers ::parse + * @uses phpDocumentor\Reflection\DocBlock\Description\PassthroughFormatter + */ + public function testInlineTagEscapingSequence() + { + $fixture = 'This is text for a description with literal {{@}link}.'; + $expected = 'This is text for a description with literal {@link}.'; + $object = new Description($fixture); + $this->assertSame($expected, $object->render()); + } + + /** + * @covers ::__construct + * @covers ::render + * @covers ::parse + * @uses phpDocumentor\Reflection\DocBlock\Tag + * @uses phpDocumentor\Reflection\DocBlock\Tags\Version + * @uses phpDocumentor\Reflection\DocBlock\Tags\Link + */ + public function testFormatterReceivesContentsAsTokens() + { + $fixture = <<shouldReceive('format')->with($expected)->andReturn($fixture); + + $object = new Description($fixture); + $this->assertSame($fixture, $object->render($formatter)); + } + + /** + * Provides a series of example strings that the parser should correctly interpret and return. + * + * @return string[][] + */ + public function provideExampleDescriptions() + { + return [ + ['This is text for a description.'], + ['This is text for a {@link http://phpdoc.org/ description} that uses an inline tag.'], + ['{@link http://phpdoc.org/ This} is text for a description that starts with an inline tag.'], + [ + 'This is text for a description with {@internal inline tag with {@link http://phpdoc.org another ' + . 'inline tag} in it}.' + ], + ['This is text for a description containing { that is literal.'], + ['This is text for a description containing {@internal inline tag that has { that is literal}.'], + ['This is text for a description with {} that is not a tag.'], + ['This is text for a description with {@internal inline tag with {} that is not an inline tag}.'], + ['This is text for a description with an {@internal inline tag with literal {{@}link{} in it}.'] + ]; + } +} diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/CoversTagTest.php b/tests/unit/DocBlock/Tag/CoversTest.php similarity index 90% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/CoversTagTest.php rename to tests/unit/DocBlock/Tag/CoversTest.php index ff257aa..c3d5dd1 100644 --- a/tests/phpDocumentor/Reflection/DocBlock/Tag/CoversTagTest.php +++ b/tests/unit/DocBlock/Tag/CoversTest.php @@ -1,7 +1,7 @@ @@ -10,7 +10,7 @@ * @link http://phpdoc.org */ -namespace phpDocumentor\Reflection\DocBlock\Tag; +namespace phpDocumentor\Reflection\DocBlock\Tags; /** * Test class for \phpDocumentor\Reflection\DocBlock\Tag\CoversTag @@ -20,10 +20,10 @@ namespace phpDocumentor\Reflection\DocBlock\Tag; * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://phpdoc.org */ -class CoversTagTest extends \PHPUnit_Framework_TestCase +class CoversTest extends \PHPUnit_Framework_TestCase { /** - * Test that the \phpDocumentor\Reflection\DocBlock\Tag\CoversTag can create + * Test that the \phpDocumentor\Reflection\DocBlock\Tags\Covers can create * a link for the covers doc block. * * @param string $type @@ -43,7 +43,7 @@ class CoversTagTest extends \PHPUnit_Framework_TestCase $exDescription, $exReference ) { - $tag = new CoversTag($type, $content); + $tag = new Covers($type, $content); $this->assertEquals($type, $tag->getName()); $this->assertEquals($exContent, $tag->getContent()); diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTagTest.php b/tests/unit/DocBlock/Tag/DeprecatedTagTest.php similarity index 100% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTagTest.php rename to tests/unit/DocBlock/Tag/DeprecatedTagTest.php diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/ExampleTagTest.php b/tests/unit/DocBlock/Tag/ExampleTagTest.php similarity index 99% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/ExampleTagTest.php rename to tests/unit/DocBlock/Tag/ExampleTagTest.php index 519a61b..9f0124d 100644 --- a/tests/phpDocumentor/Reflection/DocBlock/Tag/ExampleTagTest.php +++ b/tests/unit/DocBlock/Tag/ExampleTagTest.php @@ -1,7 +1,7 @@ @@ -47,7 +47,7 @@ class ExampleTagTest extends \PHPUnit_Framework_TestCase $exLineCount, $exFilePath ) { - $tag = new ExampleTag($type, $content); + $tag = new Example($type, $content); $this->assertEquals($type, $tag->getName()); $this->assertEquals($exContent, $tag->getContent()); diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/LinkTagTest.php b/tests/unit/DocBlock/Tag/LinkTagTest.php similarity index 100% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/LinkTagTest.php rename to tests/unit/DocBlock/Tag/LinkTagTest.php diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/MethodTagTest.php b/tests/unit/DocBlock/Tag/MethodTagTest.php similarity index 100% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/MethodTagTest.php rename to tests/unit/DocBlock/Tag/MethodTagTest.php diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/ParamTagTest.php b/tests/unit/DocBlock/Tag/ParamTagTest.php similarity index 100% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/ParamTagTest.php rename to tests/unit/DocBlock/Tag/ParamTagTest.php diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/ReturnTagTest.php b/tests/unit/DocBlock/Tag/ReturnTagTest.php similarity index 98% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/ReturnTagTest.php rename to tests/unit/DocBlock/Tag/ReturnTagTest.php index 9e2aec0..b83e1a9 100644 --- a/tests/phpDocumentor/Reflection/DocBlock/Tag/ReturnTagTest.php +++ b/tests/unit/DocBlock/Tag/ReturnTagTest.php @@ -1,7 +1,7 @@ @@ -44,7 +44,7 @@ class ReturnTagTest extends \PHPUnit_Framework_TestCase $extractedTypes, $extractedDescription ) { - $tag = new ReturnTag($type, $content); + $tag = new Return_($type, $content); $this->assertEquals($type, $tag->getName()); $this->assertEquals($extractedType, $tag->getType()); diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/SeeTagTest.php b/tests/unit/DocBlock/Tag/SeeTagTest.php similarity index 98% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/SeeTagTest.php rename to tests/unit/DocBlock/Tag/SeeTagTest.php index 6829b04..3e5a53c 100644 --- a/tests/phpDocumentor/Reflection/DocBlock/Tag/SeeTagTest.php +++ b/tests/unit/DocBlock/Tag/SeeTagTest.php @@ -1,7 +1,7 @@ @@ -43,7 +43,7 @@ class SeeTagTest extends \PHPUnit_Framework_TestCase $exDescription, $exReference ) { - $tag = new SeeTag($type, $content); + $tag = new See($type, $content); $this->assertEquals($type, $tag->getName()); $this->assertEquals($exContent, $tag->getContent()); diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/SinceTagTest.php b/tests/unit/DocBlock/Tag/SinceTagTest.php similarity index 98% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/SinceTagTest.php rename to tests/unit/DocBlock/Tag/SinceTagTest.php index 8caf25d..d424165 100644 --- a/tests/phpDocumentor/Reflection/DocBlock/Tag/SinceTagTest.php +++ b/tests/unit/DocBlock/Tag/SinceTagTest.php @@ -1,7 +1,7 @@ @@ -44,7 +44,7 @@ class SinceTagTest extends \PHPUnit_Framework_TestCase $exDescription, $exVersion ) { - $tag = new SinceTag($type, $content); + $tag = new Since($type, $content); $this->assertEquals($type, $tag->getName()); $this->assertEquals($exContent, $tag->getContent()); diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/SourceTagTest.php b/tests/unit/DocBlock/Tag/SourceTagTest.php similarity index 98% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/SourceTagTest.php rename to tests/unit/DocBlock/Tag/SourceTagTest.php index 2a40e0a..4f78355 100644 --- a/tests/phpDocumentor/Reflection/DocBlock/Tag/SourceTagTest.php +++ b/tests/unit/DocBlock/Tag/SourceTagTest.php @@ -1,7 +1,7 @@ @@ -45,7 +45,7 @@ class SourceTagTest extends \PHPUnit_Framework_TestCase $exStartingLine, $exLineCount ) { - $tag = new SourceTag($type, $content); + $tag = new Source($type, $content); $this->assertEquals($type, $tag->getName()); $this->assertEquals($exContent, $tag->getContent()); diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTagTest.php b/tests/unit/DocBlock/Tag/ThrowsTagTest.php similarity index 98% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTagTest.php rename to tests/unit/DocBlock/Tag/ThrowsTagTest.php index 3c669d5..1040275 100644 --- a/tests/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTagTest.php +++ b/tests/unit/DocBlock/Tag/ThrowsTagTest.php @@ -1,7 +1,7 @@ @@ -44,7 +44,7 @@ class ThrowsTagTest extends \PHPUnit_Framework_TestCase $extractedTypes, $extractedDescription ) { - $tag = new ThrowsTag($type, $content); + $tag = new Throws($type, $content); $this->assertEquals($type, $tag->getName()); $this->assertEquals($extractedType, $tag->getType()); diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/UsesTagTest.php b/tests/unit/DocBlock/Tag/UsesTagTest.php similarity index 97% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/UsesTagTest.php rename to tests/unit/DocBlock/Tag/UsesTagTest.php index 45868d7..a6de226 100644 --- a/tests/phpDocumentor/Reflection/DocBlock/Tag/UsesTagTest.php +++ b/tests/unit/DocBlock/Tag/UsesTagTest.php @@ -1,7 +1,7 @@ @@ -43,7 +43,7 @@ class UsesTagTest extends \PHPUnit_Framework_TestCase $exDescription, $exReference ) { - $tag = new UsesTag($type, $content); + $tag = new Uses($type, $content); $this->assertEquals($type, $tag->getName()); $this->assertEquals($exContent, $tag->getContent()); diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/VarTagTest.php b/tests/unit/DocBlock/Tag/VarTagTest.php similarity index 100% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/VarTagTest.php rename to tests/unit/DocBlock/Tag/VarTagTest.php diff --git a/tests/phpDocumentor/Reflection/DocBlock/Tag/VersionTagTest.php b/tests/unit/DocBlock/Tag/VersionTagTest.php similarity index 98% rename from tests/phpDocumentor/Reflection/DocBlock/Tag/VersionTagTest.php rename to tests/unit/DocBlock/Tag/VersionTagTest.php index e145386..58e7f00 100644 --- a/tests/phpDocumentor/Reflection/DocBlock/Tag/VersionTagTest.php +++ b/tests/unit/DocBlock/Tag/VersionTagTest.php @@ -1,7 +1,7 @@ @@ -44,7 +44,7 @@ class VersionTagTest extends \PHPUnit_Framework_TestCase $exDescription, $exVersion ) { - $tag = new VersionTag($type, $content); + $tag = new Version($type, $content); $this->assertEquals($type, $tag->getName()); $this->assertEquals($exContent, $tag->getContent()); diff --git a/tests/phpDocumentor/Reflection/DocBlock/TagTest.php b/tests/unit/DocBlock/TagTest.php similarity index 100% rename from tests/phpDocumentor/Reflection/DocBlock/TagTest.php rename to tests/unit/DocBlock/TagTest.php diff --git a/tests/phpDocumentor/Reflection/DocBlockTest.php b/tests/unit/DocBlockTest.php similarity index 98% rename from tests/phpDocumentor/Reflection/DocBlockTest.php rename to tests/unit/DocBlockTest.php index 30eedfc..92a8d8e 100644 --- a/tests/phpDocumentor/Reflection/DocBlockTest.php +++ b/tests/unit/DocBlockTest.php @@ -14,7 +14,7 @@ namespace phpDocumentor\Reflection; use phpDocumentor\Reflection\DocBlock\Context; use phpDocumentor\Reflection\DocBlock\Location; -use phpDocumentor\Reflection\DocBlock\Tag\ReturnTag; +use phpDocumentor\Reflection\DocBlock\Tag\Return_; /** * Test class for phpDocumentor\Reflection\DocBlock @@ -28,7 +28,7 @@ class DocBlockTest extends \PHPUnit_Framework_TestCase { /** * @covers \phpDocumentor\Reflection\DocBlock - * + * * @return void */ public function testConstruct() @@ -60,7 +60,7 @@ DOCBLOCK; $this->assertTrue($object->hasTag('see')); $this->assertTrue($object->hasTag('return')); $this->assertFalse($object->hasTag('category')); - + $this->assertSame('MyNamespace', $object->getContext()->getNamespace()); $this->assertSame( array('PHPDoc' => '\phpDocumentor'), @@ -128,7 +128,7 @@ DOCBLOCK; /** * @covers \phpDocumentor\Reflection\DocBlock::cleanInput - * + * * @return void */ public function testConstructOneLiner() @@ -145,7 +145,7 @@ DOCBLOCK; /** * @covers \phpDocumentor\Reflection\DocBlock::__construct - * + * * @return void */ public function testConstructFromReflector() @@ -166,7 +166,7 @@ DOCBLOCK; /** * @expectedException \InvalidArgumentException - * + * * @return void */ public function testExceptionOnInvalidObject() @@ -198,7 +198,7 @@ DOCBLOCK; /** * @covers \phpDocumentor\Reflection\DocBlock::parseTags * @expectedException \LogicException - * + * * @return void */ public function testInvalidTagBlock() @@ -218,7 +218,7 @@ class MyReflectionDocBlock extends \phpDocumentor\Reflection\DocBlock { DOCBLOCK_EXTENSION ); new \MyReflectionDocBlock(''); - + } public function testTagCaseSensitivity() @@ -263,7 +263,7 @@ DOCBLOCK; /** * @depends testConstructFromReflector * @covers \phpDocumentor\Reflection\DocBlock::getTagsByName - * + * * @return void */ public function testGetTagsByNameZeroAndOneMatch() @@ -276,7 +276,7 @@ DOCBLOCK; /** * @depends testConstructWithTagsOnly * @covers \phpDocumentor\Reflection\DocBlock::parseTags - * + * * @return void */ public function testParseMultilineTag() @@ -294,7 +294,7 @@ DOCBLOCK; /** * @depends testConstructWithTagsOnly * @covers \phpDocumentor\Reflection\DocBlock::parseTags - * + * * @return void */ public function testParseMultilineTagWithLineBreaks() @@ -309,7 +309,7 @@ DOCBLOCK; DOCBLOCK; $object = new DocBlock($fixture); $this->assertCount(1, $tags = $object->getTags()); - /** @var ReturnTag $tag */ + /** @var Return_ $tag */ $tag = reset($tags); $this->assertEquals("Content on\n multiple lines.\n\n One more, after the break.", $tag->getDescription()); } @@ -317,7 +317,7 @@ DOCBLOCK; /** * @depends testConstructWithTagsOnly * @covers \phpDocumentor\Reflection\DocBlock::getTagsByName - * + * * @return void */ public function testGetTagsByNameMultipleMatch()