Mercurial > hg > isophonics-drupal-site
comparison 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 |
comparison
equal
deleted
inserted
replaced
12:7a779792577d | 13:5fb285c0d0e3 |
---|---|
1 <?php | |
2 | |
3 /* | |
4 * This file is part of Psy Shell. | |
5 * | |
6 * (c) 2012-2018 Justin Hileman | |
7 * | |
8 * For the full copyright and license information, please view the LICENSE | |
9 * file that was distributed with this source code. | |
10 */ | |
11 | |
12 namespace Psy\Util; | |
13 | |
14 /** | |
15 * A docblock representation. | |
16 * | |
17 * Based on PHP-DocBlock-Parser by Paul Scott: | |
18 * | |
19 * {@link http://www.github.com/icio/PHP-DocBlock-Parser} | |
20 * | |
21 * @author Paul Scott <paul@duedil.com> | |
22 * @author Justin Hileman <justin@justinhileman.info> | |
23 */ | |
24 class Docblock | |
25 { | |
26 /** | |
27 * Tags in the docblock that have a whitespace-delimited number of parameters | |
28 * (such as `@param type var desc` and `@return type desc`) and the names of | |
29 * those parameters. | |
30 * | |
31 * @var array | |
32 */ | |
33 public static $vectors = [ | |
34 'throws' => ['type', 'desc'], | |
35 'param' => ['type', 'var', 'desc'], | |
36 'return' => ['type', 'desc'], | |
37 ]; | |
38 | |
39 protected $reflector; | |
40 | |
41 /** | |
42 * The description of the symbol. | |
43 * | |
44 * @var string | |
45 */ | |
46 public $desc; | |
47 | |
48 /** | |
49 * The tags defined in the docblock. | |
50 * | |
51 * The array has keys which are the tag names (excluding the @) and values | |
52 * that are arrays, each of which is an entry for the tag. | |
53 * | |
54 * In the case where the tag name is defined in {@see DocBlock::$vectors} the | |
55 * value within the tag-value array is an array in itself with keys as | |
56 * described by {@see DocBlock::$vectors}. | |
57 * | |
58 * @var array | |
59 */ | |
60 public $tags; | |
61 | |
62 /** | |
63 * The entire DocBlock comment that was parsed. | |
64 * | |
65 * @var string | |
66 */ | |
67 public $comment; | |
68 | |
69 /** | |
70 * Docblock constructor. | |
71 * | |
72 * @param \Reflector $reflector | |
73 */ | |
74 public function __construct(\Reflector $reflector) | |
75 { | |
76 $this->reflector = $reflector; | |
77 $this->setComment($reflector->getDocComment()); | |
78 } | |
79 | |
80 /** | |
81 * Set and parse the docblock comment. | |
82 * | |
83 * @param string $comment The docblock | |
84 */ | |
85 protected function setComment($comment) | |
86 { | |
87 $this->desc = ''; | |
88 $this->tags = []; | |
89 $this->comment = $comment; | |
90 | |
91 $this->parseComment($comment); | |
92 } | |
93 | |
94 /** | |
95 * Find the length of the docblock prefix. | |
96 * | |
97 * @param array $lines | |
98 * | |
99 * @return int Prefix length | |
100 */ | |
101 protected static function prefixLength(array $lines) | |
102 { | |
103 // find only lines with interesting things | |
104 $lines = array_filter($lines, function ($line) { | |
105 return substr($line, strspn($line, "* \t\n\r\0\x0B")); | |
106 }); | |
107 | |
108 // if we sort the lines, we only have to compare two items | |
109 sort($lines); | |
110 | |
111 $first = reset($lines); | |
112 $last = end($lines); | |
113 | |
114 // find the longest common substring | |
115 $count = min(strlen($first), strlen($last)); | |
116 for ($i = 0; $i < $count; $i++) { | |
117 if ($first[$i] !== $last[$i]) { | |
118 return $i; | |
119 } | |
120 } | |
121 | |
122 return $count; | |
123 } | |
124 | |
125 /** | |
126 * Parse the comment into the component parts and set the state of the object. | |
127 * | |
128 * @param string $comment The docblock | |
129 */ | |
130 protected function parseComment($comment) | |
131 { | |
132 // Strip the opening and closing tags of the docblock | |
133 $comment = substr($comment, 3, -2); | |
134 | |
135 // Split into arrays of lines | |
136 $comment = array_filter(preg_split('/\r?\n\r?/', $comment)); | |
137 | |
138 // Trim asterisks and whitespace from the beginning and whitespace from the end of lines | |
139 $prefixLength = self::prefixLength($comment); | |
140 $comment = array_map(function ($line) use ($prefixLength) { | |
141 return rtrim(substr($line, $prefixLength)); | |
142 }, $comment); | |
143 | |
144 // Group the lines together by @tags | |
145 $blocks = []; | |
146 $b = -1; | |
147 foreach ($comment as $line) { | |
148 if (self::isTagged($line)) { | |
149 $b++; | |
150 $blocks[] = []; | |
151 } elseif ($b === -1) { | |
152 $b = 0; | |
153 $blocks[] = []; | |
154 } | |
155 $blocks[$b][] = $line; | |
156 } | |
157 | |
158 // Parse the blocks | |
159 foreach ($blocks as $block => $body) { | |
160 $body = trim(implode("\n", $body)); | |
161 | |
162 if ($block === 0 && !self::isTagged($body)) { | |
163 // This is the description block | |
164 $this->desc = $body; | |
165 } else { | |
166 // This block is tagged | |
167 $tag = substr(self::strTag($body), 1); | |
168 $body = ltrim(substr($body, strlen($tag) + 2)); | |
169 | |
170 if (isset(self::$vectors[$tag])) { | |
171 // The tagged block is a vector | |
172 $count = count(self::$vectors[$tag]); | |
173 if ($body) { | |
174 $parts = preg_split('/\s+/', $body, $count); | |
175 } else { | |
176 $parts = []; | |
177 } | |
178 | |
179 // Default the trailing values | |
180 $parts = array_pad($parts, $count, null); | |
181 | |
182 // Store as a mapped array | |
183 $this->tags[$tag][] = array_combine(self::$vectors[$tag], $parts); | |
184 } else { | |
185 // The tagged block is only text | |
186 $this->tags[$tag][] = $body; | |
187 } | |
188 } | |
189 } | |
190 } | |
191 | |
192 /** | |
193 * Whether or not a docblock contains a given @tag. | |
194 * | |
195 * @param string $tag The name of the @tag to check for | |
196 * | |
197 * @return bool | |
198 */ | |
199 public function hasTag($tag) | |
200 { | |
201 return is_array($this->tags) && array_key_exists($tag, $this->tags); | |
202 } | |
203 | |
204 /** | |
205 * The value of a tag. | |
206 * | |
207 * @param string $tag | |
208 * | |
209 * @return array | |
210 */ | |
211 public function tag($tag) | |
212 { | |
213 return $this->hasTag($tag) ? $this->tags[$tag] : null; | |
214 } | |
215 | |
216 /** | |
217 * Whether or not a string begins with a @tag. | |
218 * | |
219 * @param string $str | |
220 * | |
221 * @return bool | |
222 */ | |
223 public static function isTagged($str) | |
224 { | |
225 return isset($str[1]) && $str[0] === '@' && ctype_alpha($str[1]); | |
226 } | |
227 | |
228 /** | |
229 * The tag at the beginning of a string. | |
230 * | |
231 * @param string $str | |
232 * | |
233 * @return string|null | |
234 */ | |
235 public static function strTag($str) | |
236 { | |
237 if (preg_match('/^@[a-z0-9_]+/', $str, $matches)) { | |
238 return $matches[0]; | |
239 } | |
240 } | |
241 } |