X-Git-Url: http://www.aleph1.co.uk/gitweb/?a=blobdiff_plain;f=vendor%2Fconsolidation%2Fannotated-command%2Fsrc%2FParser%2FInternal%2FBespokeDocBlockParser.php;fp=vendor%2Fconsolidation%2Fannotated-command%2Fsrc%2FParser%2FInternal%2FAbstractCommandDocBlockParser.php;h=b53545c6216316a6bfd03adb952c7a24cf9a718a;hb=9917807b03b64faf00f6a1f29dcb6eafc454efa5;hp=0543900fb6e8cbb01f576e20c36305389ca3e731;hpb=aea91e65e895364e460983b890e295aa5d5540a5;p=yaffs-website diff --git a/vendor/consolidation/annotated-command/src/Parser/Internal/AbstractCommandDocBlockParser.php b/vendor/consolidation/annotated-command/src/Parser/Internal/BespokeDocBlockParser.php similarity index 56% rename from vendor/consolidation/annotated-command/src/Parser/Internal/AbstractCommandDocBlockParser.php rename to vendor/consolidation/annotated-command/src/Parser/Internal/BespokeDocBlockParser.php index 0543900fb..b53545c62 100644 --- a/vendor/consolidation/annotated-command/src/Parser/Internal/AbstractCommandDocBlockParser.php +++ b/vendor/consolidation/annotated-command/src/Parser/Internal/BespokeDocBlockParser.php @@ -9,22 +9,9 @@ use Consolidation\AnnotatedCommand\Parser\DefaultsWithDescriptions; * DocBlock comment, and provide accessor methods for all of * the elements that are needed to create an annotated Command. */ -abstract class AbstractCommandDocBlockParser +class BespokeDocBlockParser { - /** - * @var CommandInfo - */ - protected $commandInfo; - - /** - * @var \ReflectionMethod - */ - protected $reflection; - - /** - * @var string - */ - protected $optionParamName; + protected $fqcnCache; /** * @var array @@ -33,7 +20,7 @@ abstract class AbstractCommandDocBlockParser 'command' => 'processCommandTag', 'name' => 'processCommandTag', 'arg' => 'processArgumentTag', - 'param' => 'processParamTag', + 'param' => 'processArgumentTag', 'return' => 'processReturnTag', 'option' => 'processOptionTag', 'default' => 'processDefaultTag', @@ -43,31 +30,22 @@ abstract class AbstractCommandDocBlockParser 'desc' => 'processAlternateDescriptionTag', ]; - public function __construct(CommandInfo $commandInfo, \ReflectionMethod $reflection) + public function __construct(CommandInfo $commandInfo, \ReflectionMethod $reflection, $fqcnCache = null) { $this->commandInfo = $commandInfo; $this->reflection = $reflection; + $this->fqcnCache = $fqcnCache ?: new FullyQualifiedClassCache(); } - protected function processAllTags($phpdoc) - { - // Iterate over all of the tags, and process them as necessary. - foreach ($phpdoc->getTags() as $tag) { - $processFn = [$this, 'processGenericTag']; - if (array_key_exists($tag->getName(), $this->tagProcessors)) { - $processFn = [$this, $this->tagProcessors[$tag->getName()]]; - } - $processFn($tag); - } - } - - abstract protected function getTagContents($tag); - /** * Parse the docBlock comment for this command, and set the * fields of this class with the data thereby obtained. */ - abstract public function parse(); + public function parse() + { + $doc = $this->reflection->getDocComment(); + $this->parseDocBlock($doc); + } /** * Save any tag that we do not explicitly recognize in the @@ -75,7 +53,7 @@ abstract class AbstractCommandDocBlockParser */ protected function processGenericTag($tag) { - $this->commandInfo->addAnnotation($tag->getName(), $this->getTagContents($tag)); + $this->commandInfo->addAnnotation($tag->getTag(), $tag->getContent()); } /** @@ -83,11 +61,14 @@ abstract class AbstractCommandDocBlockParser */ protected function processCommandTag($tag) { - $commandName = $this->getTagContents($tag); + if (!$tag->hasWordAndDescription($matches)) { + throw new \Exception('Could not determine command name from tag ' . (string)$tag); + } + $commandName = $matches['word']; $this->commandInfo->setName($commandName); // We also store the name in the 'other annotations' so that is is // possible to determine if the method had a @command annotation. - $this->commandInfo->addAnnotation($tag->getName(), $commandName); + $this->commandInfo->addAnnotation($tag->getTag(), $commandName); } /** @@ -99,7 +80,7 @@ abstract class AbstractCommandDocBlockParser */ protected function processAlternateDescriptionTag($tag) { - $this->commandInfo->setDescription($this->getTagContents($tag)); + $this->commandInfo->setDescription($tag->getContent()); } /** @@ -107,10 +88,13 @@ abstract class AbstractCommandDocBlockParser */ protected function processArgumentTag($tag) { - if (!$this->pregMatchNameAndDescription((string)$tag->getDescription(), $match)) { + if (!$tag->hasVariable($matches)) { + throw new \Exception('Could not determine argument name from tag ' . (string)$tag); + } + if ($matches['variable'] == $this->optionParamName()) { return; } - $this->addOptionOrArgumentTag($tag, $this->commandInfo->arguments(), $match); + $this->addOptionOrArgumentTag($tag, $this->commandInfo->arguments(), $matches['variable'], $matches['description']); } /** @@ -118,17 +102,16 @@ abstract class AbstractCommandDocBlockParser */ protected function processOptionTag($tag) { - if (!$this->pregMatchOptionNameAndDescription((string)$tag->getDescription(), $match)) { - return; + if (!$tag->hasVariable($matches)) { + throw new \Exception('Could not determine option name from tag ' . (string)$tag); } - $this->addOptionOrArgumentTag($tag, $this->commandInfo->options(), $match); + $this->addOptionOrArgumentTag($tag, $this->commandInfo->options(), $matches['variable'], $matches['description']); } - protected function addOptionOrArgumentTag($tag, DefaultsWithDescriptions $set, $nameAndDescription) + protected function addOptionOrArgumentTag($tag, DefaultsWithDescriptions $set, $name, $description) { - $variableName = $this->commandInfo->findMatchingOption($nameAndDescription['name']); - $desc = $nameAndDescription['description']; - $description = static::removeLineBreaks($desc); + $variableName = $this->commandInfo->findMatchingOption($name); + $description = static::removeLineBreaks($description); $set->add($variableName, $description); } @@ -138,11 +121,11 @@ abstract class AbstractCommandDocBlockParser */ protected function processDefaultTag($tag) { - if (!$this->pregMatchNameAndDescription((string)$tag->getDescription(), $match)) { - return; + if (!$tag->hasVariable($matches)) { + throw new \Exception('Could not determine parameter name for default value from tag ' . (string)$tag); } - $variableName = $match['name']; - $defaultValue = $this->interpretDefaultValue($match['description']); + $variableName = $matches['variable']; + $defaultValue = $this->interpretDefaultValue($matches['description']); if ($this->commandInfo->arguments()->exists($variableName)) { $this->commandInfo->arguments()->setDefaultValue($variableName, $defaultValue); return; @@ -158,9 +141,11 @@ abstract class AbstractCommandDocBlockParser */ protected function processUsageTag($tag) { - $lines = explode("\n", $this->getTagContents($tag)); - $usage = array_shift($lines); - $description = static::removeLineBreaks(implode("\n", $lines)); + $lines = explode("\n", $tag->getContent()); + $usage = trim(array_shift($lines)); + $description = static::removeLineBreaks(implode("\n", array_map(function ($line) { + return trim($line); + }, $lines))); $this->commandInfo->setExampleUsage($usage, $description); } @@ -170,7 +155,106 @@ abstract class AbstractCommandDocBlockParser */ protected function processAliases($tag) { - $this->commandInfo->setAliases((string)$tag->getDescription()); + $this->commandInfo->setAliases((string)$tag->getContent()); + } + + /** + * Store the data from a @return annotation in our argument descriptions. + */ + protected function processReturnTag($tag) + { + // The return type might be a variable -- '$this'. It will + // usually be a type, like RowsOfFields, or \Namespace\RowsOfFields. + if (!$tag->hasVariableAndDescription($matches)) { + throw new \Exception('Could not determine return type from tag ' . (string)$tag); + } + // Look at namespace and `use` statments to make returnType a fqdn + $returnType = $matches['variable']; + $returnType = $this->findFullyQualifiedClass($returnType); + $this->commandInfo->setReturnType($returnType); + } + + protected function findFullyQualifiedClass($className) + { + if (strpos($className, '\\') !== false) { + return $className; + } + + return $this->fqcnCache->qualify($this->reflection->getFileName(), $className); + } + + private function parseDocBlock($doc) + { + // Remove the leading /** and the trailing */ + $doc = preg_replace('#^\s*/\*+\s*#', '', $doc); + $doc = preg_replace('#\s*\*+/\s*#', '', $doc); + + // Nothing left? Exit. + if (empty($doc)) { + return; + } + + $tagFactory = new TagFactory(); + $lines = []; + + foreach (explode("\n", $doc) as $row) { + // Remove trailing whitespace and leading space + '*'s + $row = rtrim($row); + $row = preg_replace('#^[ \t]*\**#', '', $row); + + if (!$tagFactory->parseLine($row)) { + $lines[] = $row; + } + } + + $this->processDescriptionAndHelp($lines); + $this->processAllTags($tagFactory->getTags()); + } + + protected function processDescriptionAndHelp($lines) + { + // Trim all of the lines individually. + $lines = + array_map( + function ($line) { + return trim($line); + }, + $lines + ); + + // Everything up to the first blank line goes in the description. + $description = array_shift($lines); + while ($this->nextLineIsNotEmpty($lines)) { + $description .= ' ' . array_shift($lines); + } + + // Everything else goes in the help. + $help = trim(implode("\n", $lines)); + + $this->commandInfo->setDescription($description); + $this->commandInfo->setHelp($help); + } + + protected function nextLineIsNotEmpty($lines) + { + if (empty($lines)) { + return false; + } + + $nextLine = trim($lines[0]); + return !empty($nextLine); + } + + protected function processAllTags($tags) + { + // Iterate over all of the tags, and process them as necessary. + foreach ($tags as $tag) { + $processFn = [$this, 'processGenericTag']; + if (array_key_exists($tag->getTag(), $this->tagProcessors)) { + $processFn = [$this, $this->tagProcessors[$tag->getTag()]]; + } + $processFn($tag); + } } protected function lastParameterName() @@ -201,25 +285,6 @@ abstract class AbstractCommandDocBlockParser return $this->optionParamName; } - /** - * Store the data from a @param annotation in our argument descriptions. - */ - protected function processParamTag($tag) - { - $variableName = $tag->getVariableName(); - $variableName = str_replace('$', '', $variableName); - $description = static::removeLineBreaks((string)$tag->getDescription()); - if ($variableName == $this->optionParamName()) { - return; - } - $this->commandInfo->arguments()->add($variableName, $description); - } - - /** - * Store the data from a @return annotation in our argument descriptions. - */ - abstract protected function processReturnTag($tag); - protected function interpretDefaultValue($defaultValue) { $defaults = [ @@ -237,34 +302,6 @@ abstract class AbstractCommandDocBlockParser return $defaultValue; } - /** - * Given a docblock description in the form "$variable description", - * return the variable name and description via the 'match' parameter. - */ - protected function pregMatchNameAndDescription($source, &$match) - { - $nameRegEx = '\\$(?P[^ \t]+)[ \t]+'; - $descriptionRegEx = '(?P.*)'; - $optionRegEx = "/{$nameRegEx}{$descriptionRegEx}/s"; - - return preg_match($optionRegEx, $source, $match); - } - - /** - * Given a docblock description in the form "$variable description", - * return the variable name and description via the 'match' parameter. - */ - protected function pregMatchOptionNameAndDescription($source, &$match) - { - // Strip type and $ from the text before the @option name, if present. - $source = preg_replace('/^[a-zA-Z]* ?\\$/', '', $source); - $nameRegEx = '(?P[^ \t]+)[ \t]+'; - $descriptionRegEx = '(?P.*)'; - $optionRegEx = "/{$nameRegEx}{$descriptionRegEx}/s"; - - return preg_match($optionRegEx, $source, $match); - } - /** * Given a list that might be 'a b c' or 'a, b, c' or 'a,b,c', * convert the data into the last of these forms.