~ubuntu-branches/ubuntu/saucy/php-codesniffer/saucy

« back to all changes in this revision

Viewing changes to PHP_CodeSniffer-1.0.1/CodeSniffer/Standards/PEAR/Sniffs/Commenting/FileCommentSniff.php

  • Committer: Bazaar Package Importer
  • Author(s): Jan Wagner
  • Date: 2008-03-21 23:29:33 UTC
  • Revision ID: james.westby@ubuntu.com-20080321232933-za8kvi1bgvrvud6z
Tags: upstream-1.0.1
ImportĀ upstreamĀ versionĀ 1.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * Parses and verifies the doc comments for files.
 
4
 *
 
5
 * PHP version 5
 
6
 *
 
7
 * @category  PHP
 
8
 * @package   PHP_CodeSniffer
 
9
 * @author    Greg Sherwood <gsherwood@squiz.net>
 
10
 * @author    Marc McIntyre <mmcintyre@squiz.net>
 
11
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
 
12
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 
13
 * @version   CVS: $Id: FileCommentSniff.php,v 1.25 2007/11/26 22:11:18 squiz Exp $
 
14
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 
15
 */
 
16
 
 
17
if (class_exists('PHP_CodeSniffer_CommentParser_ClassCommentParser', true) === false) {
 
18
    throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_CommentParser_ClassCommentParser not found');
 
19
}
 
20
 
 
21
/**
 
22
 * Parses and verifies the doc comments for files.
 
23
 *
 
24
 * Verifies that :
 
25
 * <ul>
 
26
 *  <li>A doc comment exists.</li>
 
27
 *  <li>There is a blank newline after the short description.</li>
 
28
 *  <li>There is a blank newline between the long and short description.</li>
 
29
 *  <li>There is a blank newline between the long description and tags.</li>
 
30
 *  <li>A PHP version is specified.</li>
 
31
 *  <li>Check the order of the tags.</li>
 
32
 *  <li>Check the indentation of each tag.</li>
 
33
 *  <li>Check required and optional tags and the format of their content.</li>
 
34
 * </ul>
 
35
 *
 
36
 * @category  PHP
 
37
 * @package   PHP_CodeSniffer
 
38
 * @author    Greg Sherwood <gsherwood@squiz.net>
 
39
 * @author    Marc McIntyre <mmcintyre@squiz.net>
 
40
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
 
41
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 
42
 * @version   Release: 1.0.1
 
43
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 
44
 */
 
45
 
 
46
class PEAR_Sniffs_Commenting_FileCommentSniff implements PHP_CodeSniffer_Sniff
 
47
{
 
48
 
 
49
    /**
 
50
     * The header comment parser for the current file.
 
51
     *
 
52
     * @var PHP_CodeSniffer_Comment_Parser_ClassCommentParser
 
53
     */
 
54
    protected $commentParser = null;
 
55
 
 
56
    /**
 
57
     * The current PHP_CodeSniffer_File object we are processing.
 
58
     *
 
59
     * @var PHP_CodeSniffer_File
 
60
     */
 
61
    protected $currentFile = null;
 
62
 
 
63
 
 
64
    /**
 
65
     * Returns an array of tokens this test wants to listen for.
 
66
     *
 
67
     * @return array
 
68
     */
 
69
    public function register()
 
70
    {
 
71
        return array(T_OPEN_TAG);
 
72
 
 
73
    }//end register()
 
74
 
 
75
 
 
76
    /**
 
77
     * Processes this test, when one of its tokens is encountered.
 
78
     *
 
79
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
 
80
     * @param int                  $stackPtr  The position of the current token
 
81
     *                                        in the stack passed in $tokens.
 
82
     *
 
83
     * @return void
 
84
     */
 
85
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
 
86
    {
 
87
        $this->currentFile = $phpcsFile;
 
88
 
 
89
        // We are only interested if this is the first open tag.
 
90
        if ($stackPtr !== 0) {
 
91
            if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
 
92
                return;
 
93
            }
 
94
        }
 
95
 
 
96
        $tokens = $phpcsFile->getTokens();
 
97
 
 
98
        // Find the next non whitespace token.
 
99
        $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
 
100
        // Ignore vim header.
 
101
        if ($tokens[$commentStart]['code'] === T_COMMENT) {
 
102
            if (strstr($tokens[$commentStart]['content'], 'vim:') !== false) {
 
103
                $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($commentStart + 1), null, true);
 
104
            }
 
105
        }
 
106
 
 
107
        if ($tokens[$commentStart]['code'] === T_CLOSE_TAG) {
 
108
            // We are only interested if this is the first open tag.
 
109
            return;
 
110
        } else if ($tokens[$commentStart]['code'] === T_COMMENT) {
 
111
            $phpcsFile->addError('You must use "/**" style comments for a file comment', ($stackPtr + 1));
 
112
            return;
 
113
        } else if ($commentStart === false || $tokens[$commentStart]['code'] !== T_DOC_COMMENT) {
 
114
            $phpcsFile->addError('Missing file doc comment', ($stackPtr + 1));
 
115
            return;
 
116
        } else {
 
117
 
 
118
            // Extract the header comment docblock.
 
119
            $commentEnd = ($phpcsFile->findNext(T_DOC_COMMENT, ($commentStart + 1), null, true) - 1);
 
120
 
 
121
            // Check if there is only 1 doc comment between the open tag and class token.
 
122
            $nextToken   = array(
 
123
                            T_ABSTRACT,
 
124
                            T_CLASS,
 
125
                            T_FUNCTION,
 
126
                            T_DOC_COMMENT,
 
127
                           );
 
128
            $commentNext = $phpcsFile->findNext($nextToken, ($commentEnd + 1));
 
129
            if ($commentNext !== false && $tokens[$commentNext]['code'] !== T_DOC_COMMENT) {
 
130
                // Found a class token right after comment doc block.
 
131
                $newlineToken = $phpcsFile->findNext(T_WHITESPACE, ($commentEnd + 1), $commentNext, false, $phpcsFile->eolChar);
 
132
                if ($newlineToken !== false) {
 
133
                    $newlineToken = $phpcsFile->findNext(T_WHITESPACE, ($newlineToken + 1), $commentNext, false, $phpcsFile->eolChar);
 
134
                    if ($newlineToken === false) {
 
135
                        // No blank line between the class token and the doc block.
 
136
                        // The doc block is most likely a class comment.
 
137
                        $phpcsFile->addError('Missing file doc comment', ($stackPtr + 1));
 
138
                        return;
 
139
                    }
 
140
                }
 
141
            }
 
142
 
 
143
            $comment = $phpcsFile->getTokensAsString($commentStart, ($commentEnd - $commentStart + 1));
 
144
 
 
145
            // Parse the header comment docblock.
 
146
            try {
 
147
                $this->commentParser = new PHP_CodeSniffer_CommentParser_ClassCommentParser($comment, $phpcsFile);
 
148
                $this->commentParser->parse();
 
149
            } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
 
150
                $line = ($e->getLineWithinComment() + $commentStart);
 
151
                $phpcsFile->addError($e->getMessage(), $line);
 
152
                return;
 
153
            }
 
154
 
 
155
            $comment = $this->commentParser->getComment();
 
156
            if (is_null($comment) === true) {
 
157
                $error = 'File doc comment is empty';
 
158
                $phpcsFile->addError($error, $commentStart);
 
159
                return;
 
160
            }
 
161
 
 
162
            // No extra newline before short description.
 
163
            $short        = $comment->getShortComment();
 
164
            $newlineCount = 0;
 
165
            $newlineSpan  = strspn($short, $phpcsFile->eolChar);
 
166
            if ($short !== '' && $newlineSpan > 0) {
 
167
                $line  = ($newlineSpan > 1) ? 'newlines' : 'newline';
 
168
                $error = "Extra $line found before file comment short description";
 
169
                $phpcsFile->addError($error, ($commentStart + 1));
 
170
            }
 
171
 
 
172
            $newlineCount = (substr_count($short, $phpcsFile->eolChar) + 1);
 
173
 
 
174
            // Exactly one blank line between short and long description.
 
175
            $long = $comment->getLongComment();
 
176
            if (empty($long) === false) {
 
177
                $between        = $comment->getWhiteSpaceBetween();
 
178
                $newlineBetween = substr_count($between, $phpcsFile->eolChar);
 
179
                if ($newlineBetween !== 2) {
 
180
                    $error = 'There must be exactly one blank line between descriptions in file comment';
 
181
                    $phpcsFile->addError($error, ($commentStart + $newlineCount + 1));
 
182
                }
 
183
 
 
184
                $newlineCount += $newlineBetween;
 
185
            }
 
186
 
 
187
            // Exactly one blank line before tags.
 
188
            $tags = $this->commentParser->getTagOrders();
 
189
            if (count($tags) > 1) {
 
190
                $newlineSpan = $comment->getNewlineAfter();
 
191
                if ($newlineSpan !== 2) {
 
192
                    $error = 'There must be exactly one blank line before the tags in file comment';
 
193
                    if ($long !== '') {
 
194
                        $newlineCount += (substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1);
 
195
                    }
 
196
 
 
197
                    $phpcsFile->addError($error, ($commentStart + $newlineCount));
 
198
                    $short = rtrim($short, $phpcsFile->eolChar.' ');
 
199
                }
 
200
            }
 
201
 
 
202
            // Check the PHP Version.
 
203
            if (strstr(strtolower($long), 'php version') === false) {
 
204
                $error = 'PHP version not specified';
 
205
                $phpcsFile->addWarning($error, $commentEnd);
 
206
            }
 
207
 
 
208
            // Check each tag.
 
209
            $this->processTags($commentStart, $commentEnd);
 
210
        }//end if
 
211
 
 
212
    }//end process()
 
213
 
 
214
 
 
215
    /**
 
216
     * Processes each required or optional tag.
 
217
     *
 
218
     * @param int $commentStart The position in the stack where the comment started.
 
219
     * @param int $commentEnd   The position in the stack where the comment ended.
 
220
     *
 
221
     * @return void
 
222
     */
 
223
    protected function processTags($commentStart, $commentEnd)
 
224
    {
 
225
        // Tags in correct order and related info.
 
226
        $tags = array(
 
227
                 'category'   => array(
 
228
                                  'required'       => true,
 
229
                                  'allow_multiple' => false,
 
230
                                  'order_text'     => 'precedes @package',
 
231
                                 ),
 
232
                 'package'    => array(
 
233
                                  'required'       => true,
 
234
                                  'allow_multiple' => false,
 
235
                                  'order_text'     => 'follows @category',
 
236
                                 ),
 
237
                 'subpackage' => array(
 
238
                                  'required'       => false,
 
239
                                  'allow_multiple' => false,
 
240
                                  'order_text'     => 'follows @package',
 
241
                                 ),
 
242
                 'author'     => array(
 
243
                                  'required'       => true,
 
244
                                  'allow_multiple' => true,
 
245
                                  'order_text'     => 'follows @subpackage (if used) or @package',
 
246
                                 ),
 
247
                 'copyright'  => array(
 
248
                                  'required'       => false,
 
249
                                  'allow_multiple' => true,
 
250
                                  'order_text'     => 'follows @author',
 
251
                                 ),
 
252
                 'license'    => array(
 
253
                                  'required'       => true,
 
254
                                  'allow_multiple' => false,
 
255
                                  'order_text'     => 'follows @copyright (if used) or @author',
 
256
                                 ),
 
257
                 'version'    => array(
 
258
                                  'required'       => false,
 
259
                                  'allow_multiple' => false,
 
260
                                  'order_text'     => 'follows @licence',
 
261
                                 ),
 
262
                 'link'       => array(
 
263
                                  'required'       => true,
 
264
                                  'allow_multiple' => true,
 
265
                                  'order_text'     => 'follows @version',
 
266
                                 ),
 
267
                 'see'        => array(
 
268
                                  'required'       => false,
 
269
                                  'allow_multiple' => true,
 
270
                                  'order_text'     => 'follows @link',
 
271
                                 ),
 
272
                 'since'      => array(
 
273
                                  'required'       => false,
 
274
                                  'allow_multiple' => false,
 
275
                                  'order_text'     => 'follows @see (if used) or @link',
 
276
                                 ),
 
277
                 'deprecated' => array(
 
278
                                  'required'       => false,
 
279
                                  'allow_multiple' => false,
 
280
                                  'order_text'     => 'follows @since (if used) or @see (if used) or @link',
 
281
                                 ),
 
282
                );
 
283
 
 
284
        $docBlock    = (get_class($this) === 'PEAR_Sniffs_Commenting_FileCommentSniff') ? 'file' : 'class';
 
285
        $foundTags   = $this->commentParser->getTagOrders();
 
286
        $orderIndex  = 0;
 
287
        $indentation = array();
 
288
        $longestTag  = 0;
 
289
        $errorPos    = 0;
 
290
 
 
291
        foreach ($tags as $tag => $info) {
 
292
 
 
293
            // Required tag missing.
 
294
            if ($info['required'] === true && in_array($tag, $foundTags) === false) {
 
295
                $error = "Missing @$tag tag in $docBlock comment";
 
296
                $this->currentFile->addError($error, $commentEnd);
 
297
                continue;
 
298
            }
 
299
 
 
300
             // Get the line number for current tag.
 
301
            $tagName = ucfirst($tag);
 
302
            if ($info['allow_multiple'] === true) {
 
303
                $tagName .= 's';
 
304
            }
 
305
 
 
306
            $getMethod  = 'get'.$tagName;
 
307
            $tagElement = $this->commentParser->$getMethod();
 
308
            if (is_null($tagElement) === true || empty($tagElement) === true) {
 
309
                continue;
 
310
            }
 
311
 
 
312
            $errorPos = $commentStart;
 
313
            if (is_array($tagElement) === false) {
 
314
                $errorPos = ($commentStart + $tagElement->getLine());
 
315
            }
 
316
 
 
317
            // Get the tag order.
 
318
            $foundIndexes = array_keys($foundTags, $tag);
 
319
 
 
320
            if (count($foundIndexes) > 1) {
 
321
                // Multiple occurance not allowed.
 
322
                if ($info['allow_multiple'] === false) {
 
323
                    $error = "Only 1 @$tag tag is allowed in a $docBlock comment";
 
324
                    $this->currentFile->addError($error, $errorPos);
 
325
                } else {
 
326
                    // Make sure same tags are grouped together.
 
327
                    $i     = 0;
 
328
                    $count = $foundIndexes[0];
 
329
                    foreach ($foundIndexes as $index) {
 
330
                        if ($index !== $count) {
 
331
                            $errorPosIndex = ($errorPos + $tagElement[$i]->getLine());
 
332
                            $error         = "@$tag tags must be grouped together";
 
333
                            $this->currentFile->addError($error, $errorPosIndex);
 
334
                        }
 
335
 
 
336
                        $i++;
 
337
                        $count++;
 
338
                    }
 
339
                }
 
340
            }//end if
 
341
 
 
342
            // Check tag order.
 
343
            if ($foundIndexes[0] > $orderIndex) {
 
344
                $orderIndex = $foundIndexes[0];
 
345
            } else {
 
346
                if (is_array($tagElement) === true && empty($tagElement) === false) {
 
347
                    $errorPos += $tagElement[0]->getLine();
 
348
                }
 
349
 
 
350
                $orderText = $info['order_text'];
 
351
                $error     = "The @$tag tag is in the wrong order; the tag $orderText";
 
352
                $this->currentFile->addError($error, $errorPos);
 
353
            }
 
354
 
 
355
            // Store the indentation for checking.
 
356
            $len = strlen($tag);
 
357
            if ($len > $longestTag) {
 
358
                $longestTag = $len;
 
359
            }
 
360
 
 
361
            if (is_array($tagElement) === true) {
 
362
                foreach ($tagElement as $key => $element) {
 
363
                    $indentation[] = array(
 
364
                                      'tag'   => $tag,
 
365
                                      'space' => $this->getIndentation($tag, $element),
 
366
                                      'line'  => $element->getLine(),
 
367
                                     );
 
368
                }
 
369
            } else {
 
370
                $indentation[] = array(
 
371
                                  'tag'   => $tag,
 
372
                                  'space' => $this->getIndentation($tag, $tagElement),
 
373
                                 );
 
374
            }
 
375
 
 
376
            $method = 'process'.$tagName;
 
377
            if (method_exists($this, $method) === true) {
 
378
                // Process each tag if a method is defined.
 
379
                call_user_func(array($this, $method), $errorPos);
 
380
            } else {
 
381
                if (is_array($tagElement) === true) {
 
382
                    foreach ($tagElement as $key => $element) {
 
383
                        $element->process($this->currentFile, $commentStart, $docBlock);
 
384
                    }
 
385
                } else {
 
386
                     $tagElement->process($this->currentFile, $commentStart, $docBlock);
 
387
                }
 
388
            }
 
389
        }//end foreach
 
390
 
 
391
        foreach ($indentation as $indentInfo) {
 
392
            if ($indentInfo['space'] !== 0 && $indentInfo['space'] !== ($longestTag + 1)) {
 
393
                $expected     = (($longestTag - strlen($indentInfo['tag'])) + 1);
 
394
                $space        = ($indentInfo['space'] - strlen($indentInfo['tag']));
 
395
                $error        = "@$indentInfo[tag] tag comment indented incorrectly. ";
 
396
                $error       .= "Expected $expected spaces but found $space.";
 
397
                $getTagMethod = 'get'.ucfirst($indentInfo['tag']);
 
398
                if ($tags[$indentInfo['tag']]['allow_multiple'] === true) {
 
399
                    $line = $indentInfo['line'];
 
400
                } else {
 
401
                    $tagElem = $this->commentParser->$getTagMethod();
 
402
                    $line    = $tagElem->getLine();
 
403
                }
 
404
 
 
405
                $this->currentFile->addError($error, ($commentStart + $line));
 
406
            }
 
407
        }
 
408
 
 
409
    }//end processTags()
 
410
 
 
411
 
 
412
    /**
 
413
     * Get the indentation information of each tag.
 
414
     *
 
415
     * @param string                                   $tagName    The name of the doc comment element.
 
416
     * @param PHP_CodeSniffer_CommentParser_DocElement $tagElement The doc comment element.
 
417
     *
 
418
     * @return void
 
419
     */
 
420
    protected function getIndentation($tagName, $tagElement)
 
421
    {
 
422
        if ($tagElement instanceof PHP_CodeSniffer_CommentParser_SingleElement) {
 
423
            if ($tagElement->getContent() !== '') {
 
424
                return (strlen($tagName) + substr_count($tagElement->getWhitespaceBeforeContent(), ' '));
 
425
            }
 
426
        } else if ($tagElement instanceof PHP_CodeSniffer_CommentParser_PairElement) {
 
427
            if ($tagElement->getValue() !== '') {
 
428
                return (strlen($tagName) + substr_count($tagElement->getWhitespaceBeforeValue(), ' '));
 
429
            }
 
430
        }
 
431
 
 
432
        return 0;
 
433
 
 
434
    }//end getIndentation()
 
435
 
 
436
 
 
437
    /**
 
438
     * Process the category tag.
 
439
     *
 
440
     * @param int $errorPos The line number where the error occurs.
 
441
     *
 
442
     * @return void
 
443
     */
 
444
    protected function processCategory($errorPos)
 
445
    {
 
446
        $category = $this->commentParser->getCategory();
 
447
        if ($category !== null) {
 
448
            $content = $category->getContent();
 
449
            if ($content !== '') {
 
450
                if (PHP_CodeSniffer::isUnderscoreName($content) !== true) {
 
451
                    $newContent = str_replace(' ', '_', $content);
 
452
                    $nameBits   = explode('_', $newContent);
 
453
                    $firstBit   = array_shift($nameBits);
 
454
                    $newName    = ucfirst($firstBit).'_';
 
455
                    foreach ($nameBits as $bit) {
 
456
                        $newName .= ucfirst($bit).'_';
 
457
                    }
 
458
 
 
459
                    $validName = trim($newName, '_');
 
460
                    $error     = "Category name \"$content\" is not valid; consider \"$validName\" instead";
 
461
                    $this->currentFile->addError($error, $errorPos);
 
462
                }
 
463
            } else {
 
464
                $error = '@category tag must contain a name';
 
465
                $this->currentFile->addError($error, $errorPos);
 
466
            }
 
467
        }
 
468
 
 
469
    }//end processCategory()
 
470
 
 
471
 
 
472
    /**
 
473
     * Process the package tag.
 
474
     *
 
475
     * @param int $errorPos The line number where the error occurs.
 
476
     *
 
477
     * @return void
 
478
     */
 
479
    protected function processPackage($errorPos)
 
480
    {
 
481
        $package = $this->commentParser->getPackage();
 
482
        if ($package !== null) {
 
483
            $content = $package->getContent();
 
484
            if ($content !== '') {
 
485
                if (PHP_CodeSniffer::isUnderscoreName($content) !== true) {
 
486
                    $newContent = str_replace(' ', '_', $content);
 
487
                    $nameBits   = explode('_', $newContent);
 
488
                    $firstBit   = array_shift($nameBits);
 
489
                    $newName    = strtoupper($firstBit{0}).substr($firstBit, 1).'_';
 
490
                    foreach ($nameBits as $bit) {
 
491
                        $newName .= strtoupper($bit{0}).substr($bit, 1).'_';
 
492
                    }
 
493
 
 
494
                    $validName = trim($newName, '_');
 
495
                    $error     = "Package name \"$content\" is not valid; consider \"$validName\" instead";
 
496
                    $this->currentFile->addError($error, $errorPos);
 
497
                }
 
498
            } else {
 
499
                $error = '@package tag must contain a name';
 
500
                $this->currentFile->addError($error, $errorPos);
 
501
            }
 
502
        }
 
503
 
 
504
    }//end processPackage()
 
505
 
 
506
 
 
507
    /**
 
508
     * Process the subpackage tag.
 
509
     *
 
510
     * @param int $errorPos The line number where the error occurs.
 
511
     *
 
512
     * @return void
 
513
     */
 
514
    protected function processSubpackage($errorPos)
 
515
    {
 
516
        $package = $this->commentParser->getSubpackage();
 
517
        if ($package !== null) {
 
518
            $content = $package->getContent();
 
519
            if ($content !== '') {
 
520
                if (PHP_CodeSniffer::isUnderscoreName($content) !== true) {
 
521
                    $newContent = str_replace(' ', '_', $content);
 
522
                    $nameBits   = explode('_', $newContent);
 
523
                    $firstBit   = array_shift($nameBits);
 
524
                    $newName    = strtoupper($firstBit{0}).substr($firstBit, 1).'_';
 
525
                    foreach ($nameBits as $bit) {
 
526
                        $newName .= strtoupper($bit{0}).substr($bit, 1).'_';
 
527
                    }
 
528
 
 
529
                    $validName = trim($newName, '_');
 
530
                    $error     = "Subpackage name \"$content\" is not valid; consider \"$validName\" instead";
 
531
                    $this->currentFile->addError($error, $errorPos);
 
532
                }
 
533
            } else {
 
534
                $error = '@subpackage tag must contain a name';
 
535
                $this->currentFile->addError($error, $errorPos);
 
536
            }
 
537
        }
 
538
 
 
539
    }//end processSubpackage()
 
540
 
 
541
 
 
542
    /**
 
543
     * Process the author tag(s) that this header comment has.
 
544
     *
 
545
     * This function is different from other _process functions
 
546
     * as $authors is an array of SingleElements, so we work out
 
547
     * the errorPos for each element separately
 
548
     *
 
549
     * @param int $commentStart The position in the stack where
 
550
     *                          the comment started.
 
551
     *
 
552
     * @return void
 
553
     */
 
554
    protected function processAuthors($commentStart)
 
555
    {
 
556
         $authors = $this->commentParser->getAuthors();
 
557
        // Report missing return.
 
558
        if (empty($authors) === false) {
 
559
            foreach ($authors as $author) {
 
560
                $errorPos = ($commentStart + $author->getLine());
 
561
                $content  = $author->getContent();
 
562
                if ($content !== '') {
 
563
                    $local = '\da-zA-Z-_+';
 
564
                    // Dot character cannot be the first or last character in the local-part.
 
565
                    $localMiddle = $local.'.\w';
 
566
                    if (preg_match('/^([^<]*)\s+<(['.$local.']['.$localMiddle.']*['.$local.']@[\da-zA-Z][-.\w]*[\da-zA-Z]\.[a-zA-Z]{2,7})>$/', $content) === 0) {
 
567
                        $error = 'Content of the @author tag must be in the form "Display Name <username@example.com>"';
 
568
                        $this->currentFile->addError($error, $errorPos);
 
569
                    }
 
570
                } else {
 
571
                    $docBlock = (get_class($this) === 'PEAR_Sniffs_Commenting_FileCommentSniff') ? 'file' : 'class';
 
572
                    $error    = "Content missing for @author tag in $docBlock comment";
 
573
                    $this->currentFile->addError($error, $errorPos);
 
574
                }
 
575
            }
 
576
        }
 
577
 
 
578
    }//end processAuthors()
 
579
 
 
580
 
 
581
    /**
 
582
     * Process the copyright tags.
 
583
     *
 
584
     * @param int $commentStart The position in the stack where
 
585
     *                          the comment started.
 
586
     *
 
587
     * @return void
 
588
     */
 
589
    protected function processCopyrights($commentStart)
 
590
    {
 
591
        $copyrights = $this->commentParser->getCopyrights();
 
592
        foreach ($copyrights as $copyright) {
 
593
            $errorPos = ($commentStart + $copyright->getLine());
 
594
            $content  = $copyright->getContent();
 
595
            if ($content !== '') {
 
596
                $matches = array();
 
597
                if (preg_match('/^([0-9]{4})((.{1})([0-9]{4}))? (.+)$/', $content, $matches) !== 0) {
 
598
                    // Check earliest-latest year order.
 
599
                    if ($matches[3] !== '') {
 
600
                        if ($matches[3] !== '-') {
 
601
                            $error = 'A hyphen must be used between the earliest and latest year';
 
602
                            $this->currentFile->addError($error, $errorPos);
 
603
                        }
 
604
 
 
605
                        if ($matches[4] !== '' && $matches[4] < $matches[1]) {
 
606
                            $error = "Invalid year span \"$matches[1]$matches[3]$matches[4]\" found; consider \"$matches[4]-$matches[1]\" instead";
 
607
                            $this->currentFile->addWarning($error, $errorPos);
 
608
                        }
 
609
                    }
 
610
                } else {
 
611
                    $error = '@copyright tag must contain a year and the name of the copyright holder';
 
612
                    $this->currentFile->addError($error, $errorPos);
 
613
                }
 
614
            } else {
 
615
                $error = '@copyright tag must contain a year and the name of the copyright holder';
 
616
                $this->currentFile->addError($error, $errorPos);
 
617
            }//end if
 
618
        }//end if
 
619
 
 
620
    }//end processCopyrights()
 
621
 
 
622
 
 
623
    /**
 
624
     * Process the license tag.
 
625
     *
 
626
     * @param int $errorPos The line number where the error occurs.
 
627
     *
 
628
     * @return void
 
629
     */
 
630
    protected function processLicense($errorPos)
 
631
    {
 
632
        $license = $this->commentParser->getLicense();
 
633
        if ($license !== null) {
 
634
            $value   = $license->getValue();
 
635
            $comment = $license->getComment();
 
636
            if ($value === '' || $comment === '') {
 
637
                $error = '@license tag must contain a URL and a license name';
 
638
                $this->currentFile->addError($error, $errorPos);
 
639
            }
 
640
        }
 
641
 
 
642
    }//end processLicense()
 
643
 
 
644
 
 
645
    /**
 
646
     * Process the version tag.
 
647
     *
 
648
     * @param int $errorPos The line number where the error occurs.
 
649
     *
 
650
     * @return void
 
651
     */
 
652
    protected function processVersion($errorPos)
 
653
    {
 
654
        $version = $this->commentParser->getVersion();
 
655
        if ($version !== null) {
 
656
            $content = $version->getContent();
 
657
            $matches = array();
 
658
            if (empty($content) === true) {
 
659
                $error = 'Content missing for @version tag in file comment';
 
660
                $this->currentFile->addError($error, $errorPos);
 
661
            } else if (strstr($content, 'CVS:') === false && strstr($content, 'SVN:') === false) {
 
662
                $error = "Invalid version \"$content\" in file comment; consider \"CVS: <cvs_id>\" or \"SVN: <svn_id>\" instead";
 
663
                $this->currentFile->addWarning($error, $errorPos);
 
664
            }
 
665
        }
 
666
 
 
667
    }//end processVersion()
 
668
 
 
669
 
 
670
}//end class
 
671
 
 
672
?>