1<?php
2/**
3 * Squiz_Sniffs_Commenting_EmptyCatchCommentSniff.
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-2014 Squiz Pty Ltd (ABN 77 084 670 600)
12 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
13 * @link      http://pear.php.net/package/PHP_CodeSniffer
14 */
15
16/**
17 * Squiz_Sniffs_Commenting_DocCommentAlignmentSniff.
18 *
19 * Tests that the stars in a doc comment align correctly.
20 *
21 * @category  PHP
22 * @package   PHP_CodeSniffer
23 * @author    Greg Sherwood <gsherwood@squiz.net>
24 * @author    Marc McIntyre <mmcintyre@squiz.net>
25 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
26 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
27 * @version   Release: @package_version@
28 * @link      http://pear.php.net/package/PHP_CodeSniffer
29 */
30class Squiz_Sniffs_Commenting_DocCommentAlignmentSniff implements PHP_CodeSniffer_Sniff
31{
32
33    /**
34     * A list of tokenizers this sniff supports.
35     *
36     * @var array
37     */
38    public $supportedTokenizers = array(
39                                   'PHP',
40                                   'JS',
41                                  );
42
43
44    /**
45     * Returns an array of tokens this test wants to listen for.
46     *
47     * @return array
48     */
49    public function register()
50    {
51        return array(T_DOC_COMMENT_OPEN_TAG);
52
53    }//end register()
54
55
56    /**
57     * Processes this test, when one of its tokens is encountered.
58     *
59     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
60     * @param int                  $stackPtr  The position of the current token
61     *                                         in the stack passed in $tokens.
62     *
63     * @return void
64     */
65    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
66    {
67        $tokens = $phpcsFile->getTokens();
68
69        // We are only interested in function/class/interface doc block comments.
70        $ignore = PHP_CodeSniffer_Tokens::$emptyTokens;
71        if ($phpcsFile->tokenizerType === 'JS') {
72            $ignore[] = T_EQUAL;
73            $ignore[] = T_STRING;
74            $ignore[] = T_OBJECT_OPERATOR;
75        }
76
77        $nextToken = $phpcsFile->findNext($ignore, ($stackPtr + 1), null, true);
78        $ignore    = array(
79                      T_CLASS     => true,
80                      T_INTERFACE => true,
81                      T_FUNCTION  => true,
82                      T_PUBLIC    => true,
83                      T_PRIVATE   => true,
84                      T_PROTECTED => true,
85                      T_STATIC    => true,
86                      T_ABSTRACT  => true,
87                      T_PROPERTY  => true,
88                      T_OBJECT    => true,
89                      T_PROTOTYPE => true,
90                      T_VAR       => true,
91                     );
92
93        if (isset($ignore[$tokens[$nextToken]['code']]) === false) {
94            // Could be a file comment.
95            $prevToken = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
96            if ($tokens[$prevToken]['code'] !== T_OPEN_TAG) {
97                return;
98            }
99        }
100
101        // There must be one space after each star (unless it is an empty comment line)
102        // and all the stars must be aligned correctly.
103        $requiredColumn = ($tokens[$stackPtr]['column'] + 1);
104        $endComment     = $tokens[$stackPtr]['comment_closer'];
105        for ($i = ($stackPtr + 1); $i <= $endComment; $i++) {
106            if ($tokens[$i]['code'] !== T_DOC_COMMENT_STAR
107                && $tokens[$i]['code'] !== T_DOC_COMMENT_CLOSE_TAG
108            ) {
109                continue;
110            }
111
112            if ($tokens[$i]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
113                // Can't process the close tag if it is not the first thing on the line.
114                $prev = $phpcsFile->findPrevious(T_DOC_COMMENT_WHITESPACE, ($i - 1), $stackPtr, true);
115                if ($tokens[$prev]['line'] === $tokens[$i]['line']) {
116                    continue;
117                }
118            }
119
120            if ($tokens[$i]['column'] !== $requiredColumn) {
121                $error = 'Expected %s space(s) before asterisk; %s found';
122                $data  = array(
123                          ($requiredColumn - 1),
124                          ($tokens[$i]['column'] - 1),
125                         );
126                $fix   = $phpcsFile->addFixableError($error, $i, 'SpaceBeforeStar', $data);
127                if ($fix === true) {
128                    $padding = str_repeat(' ', ($requiredColumn - 1));
129                    if ($tokens[$i]['column'] === 1) {
130                        $phpcsFile->fixer->addContentBefore($i, $padding);
131                    } else {
132                        $phpcsFile->fixer->replaceToken(($i - 1), $padding);
133                    }
134                }
135            }
136
137            if ($tokens[$i]['code'] !== T_DOC_COMMENT_STAR) {
138                continue;
139            }
140
141            if ($tokens[($i + 2)]['line'] !== $tokens[$i]['line']) {
142                // Line is empty.
143                continue;
144            }
145
146            if ($tokens[($i + 1)]['code'] !== T_DOC_COMMENT_WHITESPACE) {
147                $error = 'Expected 1 space after asterisk; 0 found';
148                $fix   = $phpcsFile->addFixableError($error, $i, 'NoSpaceAfterStar');
149                if ($fix === true) {
150                    $phpcsFile->fixer->addContent($i, ' ');
151                }
152            } else if ($tokens[($i + 2)]['code'] === T_DOC_COMMENT_TAG
153                && $tokens[($i + 1)]['content'] !== ' '
154            ) {
155                $error = 'Expected 1 space after asterisk; %s found';
156                $data  = array(strlen($tokens[($i + 1)]['content']));
157                $fix   = $phpcsFile->addFixableError($error, $i, 'SpaceAfterStar', $data);
158                if ($fix === true) {
159                    $phpcsFile->fixer->replaceToken(($i + 1), ' ');
160                }
161            }
162        }//end for
163
164    }//end process()
165
166
167}//end class
168