Mercurial > hg > isophonics-drupal-site
comparison vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 /** | |
3 * phpDocumentor | |
4 * | |
5 * PHP Version 5.3 | |
6 * | |
7 * @author Mike van Riel <mike.vanriel@naenius.com> | |
8 * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) | |
9 * @license http://www.opensource.org/licenses/mit-license.php MIT | |
10 * @link http://phpdoc.org | |
11 */ | |
12 | |
13 namespace phpDocumentor\Reflection; | |
14 | |
15 use phpDocumentor\Reflection\DocBlock\Tag; | |
16 use phpDocumentor\Reflection\DocBlock\Context; | |
17 use phpDocumentor\Reflection\DocBlock\Location; | |
18 | |
19 /** | |
20 * Parses the DocBlock for any structure. | |
21 * | |
22 * @author Mike van Riel <mike.vanriel@naenius.com> | |
23 * @license http://www.opensource.org/licenses/mit-license.php MIT | |
24 * @link http://phpdoc.org | |
25 */ | |
26 class DocBlock implements \Reflector | |
27 { | |
28 /** @var string The opening line for this docblock. */ | |
29 protected $short_description = ''; | |
30 | |
31 /** | |
32 * @var DocBlock\Description The actual | |
33 * description for this docblock. | |
34 */ | |
35 protected $long_description = null; | |
36 | |
37 /** | |
38 * @var Tag[] An array containing all | |
39 * the tags in this docblock; except inline. | |
40 */ | |
41 protected $tags = array(); | |
42 | |
43 /** @var Context Information about the context of this DocBlock. */ | |
44 protected $context = null; | |
45 | |
46 /** @var Location Information about the location of this DocBlock. */ | |
47 protected $location = null; | |
48 | |
49 /** @var bool Is this DocBlock (the start of) a template? */ | |
50 protected $isTemplateStart = false; | |
51 | |
52 /** @var bool Does this DocBlock signify the end of a DocBlock template? */ | |
53 protected $isTemplateEnd = false; | |
54 | |
55 /** | |
56 * Parses the given docblock and populates the member fields. | |
57 * | |
58 * The constructor may also receive namespace information such as the | |
59 * current namespace and aliases. This information is used by some tags | |
60 * (e.g. @return, @param, etc.) to turn a relative Type into a FQCN. | |
61 * | |
62 * @param \Reflector|string $docblock A docblock comment (including | |
63 * asterisks) or reflector supporting the getDocComment method. | |
64 * @param Context $context The context in which the DocBlock | |
65 * occurs. | |
66 * @param Location $location The location within the file that this | |
67 * DocBlock occurs in. | |
68 * | |
69 * @throws \InvalidArgumentException if the given argument does not have the | |
70 * getDocComment method. | |
71 */ | |
72 public function __construct( | |
73 $docblock, | |
74 Context $context = null, | |
75 Location $location = null | |
76 ) { | |
77 if (is_object($docblock)) { | |
78 if (!method_exists($docblock, 'getDocComment')) { | |
79 throw new \InvalidArgumentException( | |
80 'Invalid object passed; the given reflector must support ' | |
81 . 'the getDocComment method' | |
82 ); | |
83 } | |
84 | |
85 $docblock = $docblock->getDocComment(); | |
86 } | |
87 | |
88 $docblock = $this->cleanInput($docblock); | |
89 | |
90 list($templateMarker, $short, $long, $tags) = $this->splitDocBlock($docblock); | |
91 $this->isTemplateStart = $templateMarker === '#@+'; | |
92 $this->isTemplateEnd = $templateMarker === '#@-'; | |
93 $this->short_description = $short; | |
94 $this->long_description = new DocBlock\Description($long, $this); | |
95 $this->parseTags($tags); | |
96 | |
97 $this->context = $context; | |
98 $this->location = $location; | |
99 } | |
100 | |
101 /** | |
102 * Strips the asterisks from the DocBlock comment. | |
103 * | |
104 * @param string $comment String containing the comment text. | |
105 * | |
106 * @return string | |
107 */ | |
108 protected function cleanInput($comment) | |
109 { | |
110 $comment = trim( | |
111 preg_replace( | |
112 '#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', | |
113 '$1', | |
114 $comment | |
115 ) | |
116 ); | |
117 | |
118 // reg ex above is not able to remove */ from a single line docblock | |
119 if (substr($comment, -2) == '*/') { | |
120 $comment = trim(substr($comment, 0, -2)); | |
121 } | |
122 | |
123 // normalize strings | |
124 $comment = str_replace(array("\r\n", "\r"), "\n", $comment); | |
125 | |
126 return $comment; | |
127 } | |
128 | |
129 /** | |
130 * Splits the DocBlock into a template marker, summary, description and block of tags. | |
131 * | |
132 * @param string $comment Comment to split into the sub-parts. | |
133 * | |
134 * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. | |
135 * @author Mike van Riel <me@mikevanriel.com> for extending the regex with template marker support. | |
136 * | |
137 * @return string[] containing the template marker (if any), summary, description and a string containing the tags. | |
138 */ | |
139 protected function splitDocBlock($comment) | |
140 { | |
141 // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This | |
142 // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the | |
143 // performance impact of running a regular expression | |
144 if (strpos($comment, '@') === 0) { | |
145 return array('', '', '', $comment); | |
146 } | |
147 | |
148 // clears all extra horizontal whitespace from the line endings to prevent parsing issues | |
149 $comment = preg_replace('/\h*$/Sum', '', $comment); | |
150 | |
151 /* | |
152 * Splits the docblock into a template marker, short description, long description and tags section | |
153 * | |
154 * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may | |
155 * occur after it and will be stripped). | |
156 * - The short description is started from the first character until a dot is encountered followed by a | |
157 * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing | |
158 * errors). This is optional. | |
159 * - The long description, any character until a new line is encountered followed by an @ and word | |
160 * characters (a tag). This is optional. | |
161 * - Tags; the remaining characters | |
162 * | |
163 * Big thanks to RichardJ for contributing this Regular Expression | |
164 */ | |
165 preg_match( | |
166 '/ | |
167 \A | |
168 # 1. Extract the template marker | |
169 (?:(\#\@\+|\#\@\-)\n?)? | |
170 | |
171 # 2. Extract the summary | |
172 (?: | |
173 (?! @\pL ) # The summary may not start with an @ | |
174 ( | |
175 [^\n.]+ | |
176 (?: | |
177 (?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines | |
178 [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line | |
179 [^\n.]+ # Include anything else | |
180 )* | |
181 \.? | |
182 )? | |
183 ) | |
184 | |
185 # 3. Extract the description | |
186 (?: | |
187 \s* # Some form of whitespace _must_ precede a description because a summary must be there | |
188 (?! @\pL ) # The description may not start with an @ | |
189 ( | |
190 [^\n]+ | |
191 (?: \n+ | |
192 (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line | |
193 [^\n]+ # Include anything else | |
194 )* | |
195 ) | |
196 )? | |
197 | |
198 # 4. Extract the tags (anything that follows) | |
199 (\s+ [\s\S]*)? # everything that follows | |
200 /ux', | |
201 $comment, | |
202 $matches | |
203 ); | |
204 array_shift($matches); | |
205 | |
206 while (count($matches) < 4) { | |
207 $matches[] = ''; | |
208 } | |
209 | |
210 return $matches; | |
211 } | |
212 | |
213 /** | |
214 * Creates the tag objects. | |
215 * | |
216 * @param string $tags Tag block to parse. | |
217 * | |
218 * @return void | |
219 */ | |
220 protected function parseTags($tags) | |
221 { | |
222 $result = array(); | |
223 $tags = trim($tags); | |
224 if ('' !== $tags) { | |
225 if ('@' !== $tags[0]) { | |
226 throw new \LogicException( | |
227 'A tag block started with text instead of an actual tag,' | |
228 . ' this makes the tag block invalid: ' . $tags | |
229 ); | |
230 } | |
231 foreach (explode("\n", $tags) as $tag_line) { | |
232 if (isset($tag_line[0]) && ($tag_line[0] === '@')) { | |
233 $result[] = $tag_line; | |
234 } else { | |
235 $result[count($result) - 1] .= "\n" . $tag_line; | |
236 } | |
237 } | |
238 | |
239 // create proper Tag objects | |
240 foreach ($result as $key => $tag_line) { | |
241 $result[$key] = Tag::createInstance(trim($tag_line), $this); | |
242 } | |
243 } | |
244 | |
245 $this->tags = $result; | |
246 } | |
247 | |
248 /** | |
249 * Gets the text portion of the doc block. | |
250 * | |
251 * Gets the text portion (short and long description combined) of the doc | |
252 * block. | |
253 * | |
254 * @return string The text portion of the doc block. | |
255 */ | |
256 public function getText() | |
257 { | |
258 $short = $this->getShortDescription(); | |
259 $long = $this->getLongDescription()->getContents(); | |
260 | |
261 if ($long) { | |
262 return "{$short}\n\n{$long}"; | |
263 } else { | |
264 return $short; | |
265 } | |
266 } | |
267 | |
268 /** | |
269 * Set the text portion of the doc block. | |
270 * | |
271 * Sets the text portion (short and long description combined) of the doc | |
272 * block. | |
273 * | |
274 * @param string $docblock The new text portion of the doc block. | |
275 * | |
276 * @return $this This doc block. | |
277 */ | |
278 public function setText($comment) | |
279 { | |
280 list(,$short, $long) = $this->splitDocBlock($comment); | |
281 $this->short_description = $short; | |
282 $this->long_description = new DocBlock\Description($long, $this); | |
283 return $this; | |
284 } | |
285 /** | |
286 * Returns the opening line or also known as short description. | |
287 * | |
288 * @return string | |
289 */ | |
290 public function getShortDescription() | |
291 { | |
292 return $this->short_description; | |
293 } | |
294 | |
295 /** | |
296 * Returns the full description or also known as long description. | |
297 * | |
298 * @return DocBlock\Description | |
299 */ | |
300 public function getLongDescription() | |
301 { | |
302 return $this->long_description; | |
303 } | |
304 | |
305 /** | |
306 * Returns whether this DocBlock is the start of a Template section. | |
307 * | |
308 * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker | |
309 * (`#@+`) that is appended directly after the opening `/**` of a DocBlock. | |
310 * | |
311 * An example of such an opening is: | |
312 * | |
313 * ``` | |
314 * /**#@+ | |
315 * * My DocBlock | |
316 * * / | |
317 * ``` | |
318 * | |
319 * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all | |
320 * elements that follow until another DocBlock is found that contains the closing marker (`#@-`). | |
321 * | |
322 * @see self::isTemplateEnd() for the check whether a closing marker was provided. | |
323 * | |
324 * @return boolean | |
325 */ | |
326 public function isTemplateStart() | |
327 { | |
328 return $this->isTemplateStart; | |
329 } | |
330 | |
331 /** | |
332 * Returns whether this DocBlock is the end of a Template section. | |
333 * | |
334 * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality. | |
335 * | |
336 * @return boolean | |
337 */ | |
338 public function isTemplateEnd() | |
339 { | |
340 return $this->isTemplateEnd; | |
341 } | |
342 | |
343 /** | |
344 * Returns the current context. | |
345 * | |
346 * @return Context | |
347 */ | |
348 public function getContext() | |
349 { | |
350 return $this->context; | |
351 } | |
352 | |
353 /** | |
354 * Returns the current location. | |
355 * | |
356 * @return Location | |
357 */ | |
358 public function getLocation() | |
359 { | |
360 return $this->location; | |
361 } | |
362 | |
363 /** | |
364 * Returns the tags for this DocBlock. | |
365 * | |
366 * @return Tag[] | |
367 */ | |
368 public function getTags() | |
369 { | |
370 return $this->tags; | |
371 } | |
372 | |
373 /** | |
374 * Returns an array of tags matching the given name. If no tags are found | |
375 * an empty array is returned. | |
376 * | |
377 * @param string $name String to search by. | |
378 * | |
379 * @return Tag[] | |
380 */ | |
381 public function getTagsByName($name) | |
382 { | |
383 $result = array(); | |
384 | |
385 /** @var Tag $tag */ | |
386 foreach ($this->getTags() as $tag) { | |
387 if ($tag->getName() != $name) { | |
388 continue; | |
389 } | |
390 | |
391 $result[] = $tag; | |
392 } | |
393 | |
394 return $result; | |
395 } | |
396 | |
397 /** | |
398 * Checks if a tag of a certain type is present in this DocBlock. | |
399 * | |
400 * @param string $name Tag name to check for. | |
401 * | |
402 * @return bool | |
403 */ | |
404 public function hasTag($name) | |
405 { | |
406 /** @var Tag $tag */ | |
407 foreach ($this->getTags() as $tag) { | |
408 if ($tag->getName() == $name) { | |
409 return true; | |
410 } | |
411 } | |
412 | |
413 return false; | |
414 } | |
415 | |
416 /** | |
417 * Appends a tag at the end of the list of tags. | |
418 * | |
419 * @param Tag $tag The tag to add. | |
420 * | |
421 * @return Tag The newly added tag. | |
422 * | |
423 * @throws \LogicException When the tag belongs to a different DocBlock. | |
424 */ | |
425 public function appendTag(Tag $tag) | |
426 { | |
427 if (null === $tag->getDocBlock()) { | |
428 $tag->setDocBlock($this); | |
429 } | |
430 | |
431 if ($tag->getDocBlock() === $this) { | |
432 $this->tags[] = $tag; | |
433 } else { | |
434 throw new \LogicException( | |
435 'This tag belongs to a different DocBlock object.' | |
436 ); | |
437 } | |
438 | |
439 return $tag; | |
440 } | |
441 | |
442 | |
443 /** | |
444 * Builds a string representation of this object. | |
445 * | |
446 * @todo determine the exact format as used by PHP Reflection and | |
447 * implement it. | |
448 * | |
449 * @return string | |
450 * @codeCoverageIgnore Not yet implemented | |
451 */ | |
452 public static function export() | |
453 { | |
454 throw new \Exception('Not yet implemented'); | |
455 } | |
456 | |
457 /** | |
458 * Returns the exported information (we should use the export static method | |
459 * BUT this throws an exception at this point). | |
460 * | |
461 * @return string | |
462 * @codeCoverageIgnore Not yet implemented | |
463 */ | |
464 public function __toString() | |
465 { | |
466 return 'Not yet implemented'; | |
467 } | |
468 } |