Subversion Repositories Applications.framework

Rev

Rev 5 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
5 aurelien 1
<?php
2
/**
3
 * Generic_Sniffs_Formatting_MultipleStatementAlignmentSniff.
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
34 aurelien 13
 * @version   CVS: $Id: MultipleStatementAlignmentSniff.php 34 2009-04-09 07:34:39Z aurelien $
5 aurelien 14
 * @link      http://pear.php.net/package/PHP_CodeSniffer
15
 */
16
 
17
/**
18
 * Generic_Sniffs_Formatting_MultipleStatementAlignmentSniff.
19
 *
20
 * Checks alignment of assignments. If there are multiple adjacent assignments,
21
 * it will check that the equals signs of each assignment are aligned. It will
22
 * display a warning to advise that the signs should be aligned.
23
 *
24
 * @category  PHP
25
 * @package   PHP_CodeSniffer
26
 * @author    Greg Sherwood <gsherwood@squiz.net>
27
 * @author    Marc McIntyre <mmcintyre@squiz.net>
28
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
29
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
30
 * @version   Release: 1.2.0RC1
31
 * @link      http://pear.php.net/package/PHP_CodeSniffer
32
 */
33
class Generic_Sniffs_Formatting_MultipleStatementAlignmentSniff implements PHP_CodeSniffer_Sniff
34
{
35
 
36
    /**
37
     * A list of tokenizers this sniff supports.
38
     *
39
     * @var array
40
     */
41
    public $supportedTokenizers = array(
42
                                   'PHP',
43
                                   'JS',
44
                                  );
45
 
46
    /**
47
     * If true, an error will be thrown; otherwise a warning.
48
     *
49
     * @var bool
50
     */
51
    protected $error = false;
52
 
53
    /**
54
     * The maximum amount of padding before the alignment is ignored.
55
     *
56
     * If the amount of padding required to align this assignment with the
57
     * surrounding assignments exceeds this number, the assignment will be
58
     * ignored and no errors or warnings will be thrown.
59
     *
60
     * @var int
61
     */
62
    protected $maxPadding = 1000;
63
 
64
    /**
65
     * If true, multi-line assignments are not checked.
66
     *
67
     * @var int
68
     */
69
    protected $ignoreMultiLine = false;
70
 
71
 
72
    /**
73
     * Returns an array of tokens this test wants to listen for.
74
     *
75
     * @return array
76
     */
77
    public function register()
78
    {
79
        return PHP_CodeSniffer_Tokens::$assignmentTokens;
80
 
81
    }//end register()
82
 
83
 
84
    /**
85
     * Processes this test, when one of its tokens is encountered.
86
     *
87
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
88
     * @param int                  $stackPtr  The position of the current token
89
     *                                        in the stack passed in $tokens.
90
     *
91
     * @return void
92
     */
93
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
94
    {
95
        $tokens = $phpcsFile->getTokens();
96
 
97
        // Ignore assignments used in a condition, like an IF or FOR.
98
        if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
99
            foreach ($tokens[$stackPtr]['nested_parenthesis'] as $start => $end) {
100
                if (isset($tokens[$start]['parenthesis_owner']) === true) {
101
                    return;
102
                }
103
            }
104
        }
105
 
106
        /*
107
            By this stage, it is known that there is an assignment on this line.
108
            We only want to process the block once we reach the last assignment,
109
            so we need to determine if there are more to follow.
110
        */
111
 
112
        // The assignment may span over multiple lines, so look for the
113
        // end of the assignment so we can check assignment blocks correctly.
114
        $lineEnd = $phpcsFile->findNext(T_SEMICOLON, ($stackPtr + 1));
115
 
116
        $nextAssign = $phpcsFile->findNext(
117
            PHP_CodeSniffer_Tokens::$assignmentTokens,
118
            ($lineEnd + 1)
119
        );
120
 
121
        if ($nextAssign !== false) {
122
            $isAssign = true;
123
            if ($tokens[$nextAssign]['line'] === ($tokens[$lineEnd]['line'] + 1)) {
124
                // Assignment may be in the same block as this one. Just make sure
125
                // it is not used in a condition, like an IF or FOR.
126
                if (isset($tokens[$nextAssign]['nested_parenthesis']) === true) {
127
                    foreach ($tokens[$nextAssign]['nested_parenthesis'] as $start => $end) {
128
                        if (isset($tokens[$start]['parenthesis_owner']) === true) {
129
                            // Not an assignment.
130
                            $isAssign = false;
131
                            break;
132
                        }
133
                    }
134
                }
135
 
136
                if ($isAssign === true) {
137
                    return;
138
                }
139
            }
140
        }
141
 
142
        // Getting here means that this is the last in a block of statements.
143
        $assignments    = array();
144
        $assignments[]  = $stackPtr;
145
        $prevAssignment = $stackPtr;
146
        $lastLine       = $tokens[$stackPtr]['line'];
147
 
148
        while (($prevAssignment = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$assignmentTokens, ($prevAssignment - 1))) !== false) {
149
 
150
            // The assignment's end token must be on the line directly
151
            // above the current one to be in the same assignment block.
152
            $lineEnd = $phpcsFile->findNext(T_SEMICOLON, ($prevAssignment + 1));
153
 
154
            // And the end token must actually belong to this assignment.
155
            $nextOpener = $phpcsFile->findNext(
156
                PHP_CodeSniffer_Tokens::$scopeOpeners,
157
                ($prevAssignment + 1)
158
            );
159
 
160
            if ($nextOpener !== false && $nextOpener < $lineEnd) {
161
                break;
162
            }
163
 
164
            if ($tokens[$lineEnd]['line'] !== ($lastLine - 1)) {
165
                break;
166
            }
167
 
168
            // Make sure it is not assigned inside a condition (eg. IF, FOR).
169
            if (isset($tokens[$prevAssignment]['nested_parenthesis']) === true) {
170
                foreach ($tokens[$prevAssignment]['nested_parenthesis'] as $start => $end) {
171
                    if (isset($tokens[$start]['parenthesis_owner']) === true) {
172
                        break(2);
173
                    }
174
                }
175
            }
176
 
177
            $assignments[] = $prevAssignment;
178
            $lastLine      = $tokens[$prevAssignment]['line'];
179
        }//end while
180
 
181
        $assignmentData      = array();
182
        $maxAssignmentLength = 0;
183
        $maxVariableLength   = 0;
184
 
185
        foreach ($assignments as $assignment) {
186
            $prev = $phpcsFile->findPrevious(
187
                PHP_CodeSniffer_Tokens::$emptyTokens,
188
                ($assignment - 1),
189
                null,
190
                true
191
            );
192
 
193
            $endColumn = $tokens[($prev + 1)]['column'];
194
 
195
            if ($maxVariableLength < $endColumn) {
196
                $maxVariableLength = $endColumn;
197
            }
198
 
199
            if ($maxAssignmentLength < strlen($tokens[$assignment]['content'])) {
200
                $maxAssignmentLength = strlen($tokens[$assignment]['content']);
201
            }
202
 
203
            $assignmentData[$assignment]
204
                = array(
205
                   'variable_length'   => $endColumn,
206
                   'assignment_length' => strlen($tokens[$assignment]['content']),
207
                  );
208
        }//end foreach
209
 
210
        foreach ($assignmentData as $assignment => $data) {
211
            if ($data['assignment_length'] === $maxAssignmentLength) {
212
                if ($data['variable_length'] === $maxVariableLength) {
213
                    // The assignment is the longest possible, so the column that
214
                    // everything has to align to is based on it.
215
                    $column = ($maxVariableLength + 1);
216
                    break;
217
                } else {
218
                    // The assignment token is the longest out of all of the
219
                    // assignments, but the variable name is not, so the column
220
                    // the start at can go back more to cover the space
221
                    // between the variable name and the assigment operator.
222
                    $column = ($maxVariableLength - ($maxAssignmentLength - 1) + 1);
223
                }
224
            }
225
        }
226
 
227
        // Determine the actual position that each equals sign should be in.
228
        foreach ($assignments as $assignment) {
229
            // Actual column takes into account the length of the assignment operator.
230
            $actualColumn = ($column + $maxAssignmentLength - strlen($tokens[$assignment]['content']));
231
            if ($tokens[$assignment]['column'] !== $actualColumn) {
232
                $prev = $phpcsFile->findPrevious(
233
                    PHP_CodeSniffer_Tokens::$emptyTokens,
234
                    ($assignment - 1),
235
                    null,
236
                    true
237
                );
238
 
239
                $expected = ($actualColumn - $tokens[($prev + 1)]['column']);
240
 
241
                if ($tokens[$assignment]['line'] !== $tokens[$prev]['line']) {
242
                    // Instead of working out how many spaces there are
243
                    // across new lines, the error message becomes more
244
                    // generic below.
245
                    $found = null;
246
                } else {
247
                    $found = ($tokens[$assignment]['column'] - $tokens[($prev + 1)]['column']);
248
                }
249
 
250
                // If the expected number of spaces for alignment exceeds the
251
                // maxPadding rule, we can ignore this assignment.
252
                if ($expected > $this->maxPadding) {
253
                    continue;
254
                }
255
 
256
                // Skip multi-line assignments if required.
257
                if ($found === null && $this->ignoreMultiLine === true) {
258
                    continue;
259
                }
260
 
261
                $expected .= ($expected === 1) ? ' space' : ' spaces';
262
                if ($found === null) {
263
                    $found = 'a new line';
264
                } else {
265
                    $found .= ($found === 1) ? ' space' : ' spaces';
266
                }
267
 
268
                if (count($assignments) === 1) {
269
                    $error = "Equals sign not aligned correctly; expected $expected but found $found";
270
                } else {
271
                    $error = "Equals sign not aligned with surrounding assignments; expected $expected but found $found";
272
                }
273
 
274
                if ($this->error === true) {
275
                    $phpcsFile->addError($error, $assignment);
276
                } else {
277
                    $phpcsFile->addWarning($error, $assignment);
278
                }
279
            }//end if
280
        }//end foreach
281
 
282
    }//end process()
283
 
284
 
285
}//end class
286
 
287
?>