@@ -46,6 +46,12 @@ class DocBlock implements \Reflector
46
46
/** @var Location Information about the location of this DocBlock. */
47
47
protected $ location = null ;
48
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
+
49
55
/**
50
56
* Parses the given docblock and populates the member fields.
51
57
*
@@ -81,7 +87,9 @@ public function __construct(
81
87
82
88
$ docblock = $ this ->cleanInput ($ docblock );
83
89
84
- list ($ short , $ long , $ tags ) = $ this ->splitDocBlock ($ docblock );
90
+ list ($ templateMarker , $ short , $ long , $ tags ) = $ this ->splitDocBlock ($ docblock );
91
+ $ this ->isTemplateStart = $ templateMarker === '#@+ ' ;
92
+ $ this ->isTemplateEnd = $ templateMarker === '#@- ' ;
85
93
$ this ->short_description = $ short ;
86
94
$ this ->long_description = new DocBlock \Description ($ long , $ this );
87
95
$ this ->parseTags ($ tags );
@@ -119,74 +127,86 @@ protected function cleanInput($comment)
119
127
}
120
128
121
129
/**
122
- * Splits the DocBlock into a short description, long description and
123
- * block of tags.
130
+ * Splits the DocBlock into a template marker, summary, description and block of tags.
124
131
*
125
132
* @param string $comment Comment to split into the sub-parts.
126
133
*
127
- * @author RichardJ Special thanks to RichardJ for the regex responsible
128
- * for the split .
134
+ * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
135
+ * @author Mike van Riel <[email protected] > for extending the regex with template marker support .
129
136
*
130
- * @return string[] containing the short-, long description and an element
131
- * containing the tags.
137
+ * @return string[] containing the template marker (if any), summary, description and a string containing the tags.
132
138
*/
133
139
protected function splitDocBlock ($ comment )
134
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
135
144
if (strpos ($ comment , '@ ' ) === 0 ) {
136
- $ matches = array ('' , '' , $ comment );
137
- } else {
138
- // clears all extra horizontal whitespace from the line endings
139
- // to prevent parsing issues
140
- $ comment = preg_replace ('/\h*$/Sum ' , '' , $ comment );
141
-
142
- /*
143
- * Splits the docblock into a short description, long description and
144
- * tags section
145
- * - The short description is started from the first character until
146
- * a dot is encountered followed by a newline OR
147
- * two consecutive newlines (horizontal whitespace is taken into
148
- * account to consider spacing errors)
149
- * - The long description, any character until a new line is
150
- * encountered followed by an @ and word characters (a tag).
151
- * This is optional.
152
- * - Tags; the remaining characters
153
- *
154
- * Big thanks to RichardJ for contributing this Regular Expression
155
- */
156
- preg_match (
157
- '/
158
- \A (
159
- [^\n.]+
160
- (?:
161
- (?! \. \n | \n{2} ) # disallow the first seperator here
162
- [\n.] (?! [ \t]* @\pL ) # disallow second seperator
163
- [^\n.]+
164
- )*
165
- \.?
166
- )
167
- (?:
168
- \s* # first seperator (actually newlines but it \'s all whitespace)
169
- (?! @\pL ) # disallow the rest, to make sure this one doesn \'t match,
170
- #if it doesn \'t exist
171
- (
172
- [^\n]+
173
- (?: \n+
174
- (?! [ \t]* @\pL ) # disallow second seperator (@param)
175
- [^\n]+
176
- )*
177
- )
178
- )?
179
- (\s+ [\s\S]*)? # everything that follows
180
- /ux ' ,
181
- $ comment ,
182
- $ matches
183
- );
184
- array_shift ($ matches );
145
+ return array ('' , '' , '' , $ comment );
185
146
}
186
147
187
- while (count ($ matches ) < 3 ) {
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 ) {
188
207
$ matches [] = '' ;
189
208
}
209
+
190
210
return $ matches ;
191
211
}
192
212
@@ -257,7 +277,7 @@ public function getText()
257
277
*/
258
278
public function setText ($ comment )
259
279
{
260
- list ($ short , $ long ) = $ this ->splitDocBlock ($ comment );
280
+ list (, $ short , $ long ) = $ this ->splitDocBlock ($ comment );
261
281
$ this ->short_description = $ short ;
262
282
$ this ->long_description = new DocBlock \Description ($ long , $ this );
263
283
return $ this ;
@@ -282,6 +302,44 @@ public function getLongDescription()
282
302
return $ this ->long_description ;
283
303
}
284
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
+
285
343
/**
286
344
* Returns the current context.
287
345
*
0 commit comments