Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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
13
 * @version   CVS: $Id: MultipleStatementAlignmentSniff.php 288187 2009-09-09 06:12:33Z squiz $
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.1
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 just check for a single space as no
252
                // alignment is required.
253
                if ($expected > $this->maxPadding) {
254
                    if ($found === 1) {
255
                        continue;
256
                    } else {
257
                        $expected = 1;
258
                    }
259
                }
260
 
261
                // Skip multi-line assignments if required.
262
                if ($found === null && $this->ignoreMultiLine === true) {
263
                    continue;
264
                }
265
 
266
                $expected .= ($expected === 1) ? ' space' : ' spaces';
267
                if ($found === null) {
268
                    $found = 'a new line';
269
                } else {
270
                    $found .= ($found === 1) ? ' space' : ' spaces';
271
                }
272
 
273
                if (count($assignments) === 1) {
274
                    $error = "Equals sign not aligned correctly; expected $expected but found $found";
275
                } else {
276
                    $error = "Equals sign not aligned with surrounding assignments; expected $expected but found $found";
277
                }
278
 
279
                if ($this->error === true) {
280
                    $phpcsFile->addError($error, $assignment);
281
                } else {
282
                    $phpcsFile->addWarning($error, $assignment);
283
                }
284
            }//end if
285
        }//end foreach
286
 
287
    }//end process()
288
 
289
 
290
}//end class
291
 
292
?>