X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=vendor%2Fpsy%2Fpsysh%2Fsrc%2FUtil%2FDocblock.php;fp=vendor%2Fpsy%2Fpsysh%2Fsrc%2FUtil%2FDocblock.php;h=c7989471dbc0951bea266becc34491294a609798;hp=0000000000000000000000000000000000000000;hb=af6d1fb995500ae68849458ee10d66abbdcfb252;hpb=680c79a86e3ed402f263faeac92e89fb6d9edcc0 diff --git a/vendor/psy/psysh/src/Util/Docblock.php b/vendor/psy/psysh/src/Util/Docblock.php new file mode 100644 index 000000000..c7989471d --- /dev/null +++ b/vendor/psy/psysh/src/Util/Docblock.php @@ -0,0 +1,241 @@ + + * @author Justin Hileman + */ +class Docblock +{ + /** + * Tags in the docblock that have a whitespace-delimited number of parameters + * (such as `@param type var desc` and `@return type desc`) and the names of + * those parameters. + * + * @var array + */ + public static $vectors = [ + 'throws' => ['type', 'desc'], + 'param' => ['type', 'var', 'desc'], + 'return' => ['type', 'desc'], + ]; + + protected $reflector; + + /** + * The description of the symbol. + * + * @var string + */ + public $desc; + + /** + * The tags defined in the docblock. + * + * The array has keys which are the tag names (excluding the @) and values + * that are arrays, each of which is an entry for the tag. + * + * In the case where the tag name is defined in {@see DocBlock::$vectors} the + * value within the tag-value array is an array in itself with keys as + * described by {@see DocBlock::$vectors}. + * + * @var array + */ + public $tags; + + /** + * The entire DocBlock comment that was parsed. + * + * @var string + */ + public $comment; + + /** + * Docblock constructor. + * + * @param \Reflector $reflector + */ + public function __construct(\Reflector $reflector) + { + $this->reflector = $reflector; + $this->setComment($reflector->getDocComment()); + } + + /** + * Set and parse the docblock comment. + * + * @param string $comment The docblock + */ + protected function setComment($comment) + { + $this->desc = ''; + $this->tags = []; + $this->comment = $comment; + + $this->parseComment($comment); + } + + /** + * Find the length of the docblock prefix. + * + * @param array $lines + * + * @return int Prefix length + */ + protected static function prefixLength(array $lines) + { + // find only lines with interesting things + $lines = array_filter($lines, function ($line) { + return substr($line, strspn($line, "* \t\n\r\0\x0B")); + }); + + // if we sort the lines, we only have to compare two items + sort($lines); + + $first = reset($lines); + $last = end($lines); + + // find the longest common substring + $count = min(strlen($first), strlen($last)); + for ($i = 0; $i < $count; $i++) { + if ($first[$i] !== $last[$i]) { + return $i; + } + } + + return $count; + } + + /** + * Parse the comment into the component parts and set the state of the object. + * + * @param string $comment The docblock + */ + protected function parseComment($comment) + { + // Strip the opening and closing tags of the docblock + $comment = substr($comment, 3, -2); + + // Split into arrays of lines + $comment = array_filter(preg_split('/\r?\n\r?/', $comment)); + + // Trim asterisks and whitespace from the beginning and whitespace from the end of lines + $prefixLength = self::prefixLength($comment); + $comment = array_map(function ($line) use ($prefixLength) { + return rtrim(substr($line, $prefixLength)); + }, $comment); + + // Group the lines together by @tags + $blocks = []; + $b = -1; + foreach ($comment as $line) { + if (self::isTagged($line)) { + $b++; + $blocks[] = []; + } elseif ($b === -1) { + $b = 0; + $blocks[] = []; + } + $blocks[$b][] = $line; + } + + // Parse the blocks + foreach ($blocks as $block => $body) { + $body = trim(implode("\n", $body)); + + if ($block === 0 && !self::isTagged($body)) { + // This is the description block + $this->desc = $body; + } else { + // This block is tagged + $tag = substr(self::strTag($body), 1); + $body = ltrim(substr($body, strlen($tag) + 2)); + + if (isset(self::$vectors[$tag])) { + // The tagged block is a vector + $count = count(self::$vectors[$tag]); + if ($body) { + $parts = preg_split('/\s+/', $body, $count); + } else { + $parts = []; + } + + // Default the trailing values + $parts = array_pad($parts, $count, null); + + // Store as a mapped array + $this->tags[$tag][] = array_combine(self::$vectors[$tag], $parts); + } else { + // The tagged block is only text + $this->tags[$tag][] = $body; + } + } + } + } + + /** + * Whether or not a docblock contains a given @tag. + * + * @param string $tag The name of the @tag to check for + * + * @return bool + */ + public function hasTag($tag) + { + return is_array($this->tags) && array_key_exists($tag, $this->tags); + } + + /** + * The value of a tag. + * + * @param string $tag + * + * @return array + */ + public function tag($tag) + { + return $this->hasTag($tag) ? $this->tags[$tag] : null; + } + + /** + * Whether or not a string begins with a @tag. + * + * @param string $str + * + * @return bool + */ + public static function isTagged($str) + { + return isset($str[1]) && $str[0] === '@' && ctype_alpha($str[1]); + } + + /** + * The tag at the beginning of a string. + * + * @param string $str + * + * @return string|null + */ + public static function strTag($str) + { + if (preg_match('/^@[a-z0-9_]+/', $str, $matches)) { + return $matches[0]; + } + } +}