Mercurial > hg > isophonics-drupal-site
diff vendor/psy/psysh/src/Util/Docblock.php @ 13:5fb285c0d0e3
Update Drupal core to 8.4.7 via Composer. Security update; I *think* we've
been lucky to get away with this so far, as we don't support self-registration
which seems to be used by the so-called "drupalgeddon 2" attack that 8.4.5
was vulnerable to.
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:33:26 +0100 |
parents | |
children | 129ea1e6d783 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/psy/psysh/src/Util/Docblock.php Mon Apr 23 09:33:26 2018 +0100 @@ -0,0 +1,241 @@ +<?php + +/* + * This file is part of Psy Shell. + * + * (c) 2012-2018 Justin Hileman + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Psy\Util; + +/** + * A docblock representation. + * + * Based on PHP-DocBlock-Parser by Paul Scott: + * + * {@link http://www.github.com/icio/PHP-DocBlock-Parser} + * + * @author Paul Scott <paul@duedil.com> + * @author Justin Hileman <justin@justinhileman.info> + */ +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]; + } + } +}