nginxsonarrradarrplexorganizrdashboardbookmarkapplication-dashboardmuximuxlandingpagestartpagelandinghtpcserverhomepagesabnzbdheimdallembycouchpotatonzbget
		
		
		
		
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							277 lines
						
					
					
						
							9.1 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							277 lines
						
					
					
						
							9.1 KiB
						
					
					
				| <?php | |
| /** | |
|  * This file is part of phpDocumentor. | |
|  * | |
|  * For the full copyright and license information, please view the LICENSE | |
|  * file that was distributed with this source code. | |
|  * | |
|  * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org> | |
|  * @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\StandardTagFactory; | |
| use phpDocumentor\Reflection\DocBlock\Tag; | |
| use phpDocumentor\Reflection\DocBlock\TagFactory; | |
| use Webmozart\Assert\Assert; | |
| 
 | |
| final class DocBlockFactory implements DocBlockFactoryInterface | |
| { | |
|     /** @var DocBlock\DescriptionFactory */ | |
|     private $descriptionFactory; | |
| 
 | |
|     /** @var DocBlock\TagFactory */ | |
|     private $tagFactory; | |
| 
 | |
|     /** | |
|      * Initializes this factory with the required subcontractors. | |
|      * | |
|      * @param DescriptionFactory $descriptionFactory | |
|      * @param TagFactory         $tagFactory | |
|      */ | |
|     public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory) | |
|     { | |
|         $this->descriptionFactory = $descriptionFactory; | |
|         $this->tagFactory = $tagFactory; | |
|     } | |
| 
 | |
|     /** | |
|      * Factory method for easy instantiation. | |
|      * | |
|      * @param string[] $additionalTags | |
|      * | |
|      * @return DocBlockFactory | |
|      */ | |
|     public static function createInstance(array $additionalTags = []) | |
|     { | |
|         $fqsenResolver = new FqsenResolver(); | |
|         $tagFactory = new StandardTagFactory($fqsenResolver); | |
|         $descriptionFactory = new DescriptionFactory($tagFactory); | |
| 
 | |
|         $tagFactory->addService($descriptionFactory); | |
|         $tagFactory->addService(new TypeResolver($fqsenResolver)); | |
| 
 | |
|         $docBlockFactory = new self($descriptionFactory, $tagFactory); | |
|         foreach ($additionalTags as $tagName => $tagHandler) { | |
|             $docBlockFactory->registerTagHandler($tagName, $tagHandler); | |
|         } | |
| 
 | |
|         return $docBlockFactory; | |
|     } | |
| 
 | |
|     /** | |
|      * @param object|string $docblock A string containing the DocBlock to parse or an object supporting the | |
|      *                                getDocComment method (such as a ReflectionClass object). | |
|      * @param Types\Context $context | |
|      * @param Location      $location | |
|      * | |
|      * @return DocBlock | |
|      */ | |
|     public function create($docblock, Types\Context $context = null, Location $location = null) | |
|     { | |
|         if (is_object($docblock)) { | |
|             if (!method_exists($docblock, 'getDocComment')) { | |
|                 $exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method'; | |
|                 throw new \InvalidArgumentException($exceptionMessage); | |
|             } | |
| 
 | |
|             $docblock = $docblock->getDocComment(); | |
|         } | |
| 
 | |
|         Assert::stringNotEmpty($docblock); | |
| 
 | |
|         if ($context === null) { | |
|             $context = new Types\Context(''); | |
|         } | |
| 
 | |
|         $parts = $this->splitDocBlock($this->stripDocComment($docblock)); | |
|         list($templateMarker, $summary, $description, $tags) = $parts; | |
| 
 | |
|         return new DocBlock( | |
|             $summary, | |
|             $description ? $this->descriptionFactory->create($description, $context) : null, | |
|             array_filter($this->parseTagBlock($tags, $context), function ($tag) { | |
|                 return $tag instanceof Tag; | |
|             }), | |
|             $context, | |
|             $location, | |
|             $templateMarker === '#@+', | |
|             $templateMarker === '#@-' | |
|         ); | |
|     } | |
| 
 | |
|     public function registerTagHandler($tagName, $handler) | |
|     { | |
|         $this->tagFactory->registerTagHandler($tagName, $handler); | |
|     } | |
| 
 | |
|     /** | |
|      * 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)); | |
|         } | |
| 
 | |
|         return str_replace(["\r\n", "\r"], "\n", $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 <me@mikevanriel.com> 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 ['', '', '', $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. | |
|      * @param Types\Context $context Context of the parsed Tag | |
|      * | |
|      * @return DocBlock\Tag[] | |
|      */ | |
|     private function parseTagBlock($tags, Types\Context $context) | |
|     { | |
|         $tags = $this->filterTagBlock($tags); | |
|         if (!$tags) { | |
|             return []; | |
|         } | |
| 
 | |
|         $result = $this->splitTagBlockIntoTagLines($tags); | |
|         foreach ($result as $key => $tagLine) { | |
|             $result[$key] = $this->tagFactory->create(trim($tagLine), $context); | |
|         } | |
| 
 | |
|         return $result; | |
|     } | |
| 
 | |
|     /** | |
|      * @param string $tags | |
|      * | |
|      * @return string[] | |
|      */ | |
|     private function splitTagBlockIntoTagLines($tags) | |
|     { | |
|         $result = []; | |
|         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]) { | |
|             // @codeCoverageIgnoreStart | |
|             // Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that | |
|             // we didn't foresee. | |
|             throw new \LogicException('A tag block started with text instead of an at-sign(@): ' . $tags); | |
|             // @codeCoverageIgnoreEnd | |
|         } | |
| 
 | |
|         return $tags; | |
|     } | |
| }
 | |
| 
 |