Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * Squiz_Sniffs_PHP_InnerFunctionsSniff.
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: NonExecutableCodeSniff.php 289552 2009-10-11 23:23:51Z squiz $
14
 * @link      http://pear.php.net/package/PHP_CodeSniffer
15
 */
16
 
17
/**
18
 * Squiz_Sniffs_PHP_NonExecutableCodeSniff.
19
 *
20
 * Warns about code that can never been executed. This happens when a function
21
 * returns before the code, or a break ends execution of a statement etc.
22
 *
23
 * @category  PHP
24
 * @package   PHP_CodeSniffer
25
 * @author    Greg Sherwood <gsherwood@squiz.net>
26
 * @author    Marc McIntyre <mmcintyre@squiz.net>
27
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
28
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
29
 * @version   Release: 1.2.1
30
 * @link      http://pear.php.net/package/PHP_CodeSniffer
31
 */
32
class Squiz_Sniffs_PHP_NonExecutableCodeSniff implements PHP_CodeSniffer_Sniff
33
{
34
 
35
 
36
    /**
37
     * Returns an array of tokens this test wants to listen for.
38
     *
39
     * @return array
40
     */
41
    public function register()
42
    {
43
        return array(
44
                T_BREAK,
45
                T_CONTINUE,
46
                T_RETURN,
47
                T_EXIT,
48
               );
49
 
50
    }//end register()
51
 
52
 
53
    /**
54
     * Processes this test, when one of its tokens is encountered.
55
     *
56
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
57
     * @param int                  $stackPtr  The position of the current token in
58
     *                                        the stack passed in $tokens.
59
     *
60
     * @return void
61
     */
62
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
63
    {
64
        $tokens = $phpcsFile->getTokens();
65
 
66
        if ($tokens[$stackPtr]['code'] === T_BREAK
67
            && isset($tokens[$stackPtr]['scope_opener']) === true
68
        ) {
69
            // This break closes the scope of a CASE or DEFAULT statement
70
            // so any code between this token and the next CASE, DEFAULT or
71
            // end of SWITCH token will not be executable.
72
            $next = $phpcsFile->findNext(
73
                array(T_CASE, T_DEFAULT, T_CLOSE_CURLY_BRACKET),
74
                ($stackPtr + 1)
75
            );
76
 
77
            if ($next !== false) {
78
                $lastLine = $tokens[($stackPtr + 1)]['line'];
79
                for ($i = ($stackPtr + 1); $i < $next; $i++) {
80
                    if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
81
                        continue;
82
                    }
83
 
84
                    $line = $tokens[$i]['line'];
85
                    if ($line > $lastLine) {
86
                        $type    = substr($tokens[$stackPtr]['type'], 2);
87
                        $warning = "Code after $type statement cannot be executed";
88
                        $phpcsFile->addWarning($warning, $i);
89
                        $lastLine = $line;
90
                    }
91
                }
92
            }//end if
93
 
94
            // That's all we have to check for these types of BREAK statements.
95
            return;
96
        }//end if
97
 
98
        // This token may be part of an inline condition.
99
        // If we find a closing parenthesis that belongs to a condition
100
        // we should ignore this token.
101
        $prev = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
102
        if (isset($tokens[$prev]['parenthesis_owner']) === true) {
103
            $owner  = $tokens[$prev]['parenthesis_owner'];
104
            $ignore = array(
105
                       T_IF,
106
                       T_ELSE,
107
                       T_ELSEIF,
108
                      );
109
            if (in_array($tokens[$owner]['code'], $ignore) === true) {
110
                return;
111
            }
112
        }
113
 
114
        $ourConditions = array_keys($tokens[$stackPtr]['conditions']);
115
        $ourTokens     = $this->register();
116
        $hasConditions = empty($ourConditions);
117
 
118
        // Skip this token if it is non-executable code itself.
119
        if ($hasConditions === false) {
120
            for ($i = ($stackPtr - 1); $i >= 1; $i--) {
121
                // Skip tokens that close the scope. They don't end
122
                // the execution of code.
123
                if (isset($tokens[$i]['scope_opener']) === true) {
124
                    continue;
125
                }
126
 
127
                // Skip tokens that do not end execution.
128
                if (in_array($tokens[$i]['code'], $ourTokens) === false) {
129
                    continue;
130
                }
131
 
132
                if (empty($tokens[$i]['conditions']) === true) {
133
                    // Found an end of execution token in the global
134
                    // scope, so it will be executed before us.
135
                    return;
136
                }
137
 
138
                // If the deepest condition this token is in also happens
139
                // to be a condition we are in, it will get executed before us.
140
                $conditions = array_keys($tokens[$i]['conditions']);
141
                $condition  = array_pop($conditions);
142
                if (in_array($condition, $ourConditions) === true) {
143
                    return;
144
                }
145
            }//end for
146
        } else {
147
            // Look for other end of execution tokens in the global scope.
148
            for ($i = ($stackPtr - 1); $i >= 1; $i--) {
149
                if (in_array($tokens[$i]['code'], $ourTokens) === false) {
150
                    continue;
151
                }
152
 
153
                if (empty($tokens[$i]['conditions']) === false) {
154
                    continue;
155
                }
156
 
157
                // Another end of execution token was before us in the
158
                // global scope, so we are not executable.
159
                return;
160
            }
161
        }//end if
162
 
163
        if ($hasConditions === false) {
164
            $condition = array_pop($ourConditions);
165
 
166
            if (isset($tokens[$condition]['scope_closer']) === false) {
167
                return;
168
            }
169
 
170
            $closer = $tokens[$condition]['scope_closer'];
171
 
172
            // If the closer for our condition is shared with other openers,
173
            // we will need to throw errors from this token to the next
174
            // shared opener (if there is one), not to the scope closer.
175
            $nextOpener = null;
176
            for ($i = ($stackPtr + 1); $i < $closer; $i++) {
177
                if (isset($tokens[$i]['scope_closer']) === true) {
178
                    if ($tokens[$i]['scope_closer'] === $closer) {
179
                        // We found an opener that shares the same
180
                        // closing token as us.
181
                        $nextOpener = $i;
182
                        break;
183
                    }
184
                }
185
            }//end for
186
 
187
            $start = $phpcsFile->findNext(T_SEMICOLON, ($stackPtr + 1));
188
 
189
            if ($nextOpener === null) {
190
                $end = $closer;
191
            } else {
192
                $end = $nextOpener;
193
            }
194
        } else {
195
            // This token is in the global scope.
196
            if ($tokens[$stackPtr]['code'] === T_BREAK) {
197
                return;
198
            }
199
 
200
            // Throw an error for all lines until the end of the file.
201
            $start = $phpcsFile->findNext(T_SEMICOLON, ($stackPtr + 1));
202
            $end   = ($phpcsFile->numTokens - 1);
203
        }//end if
204
 
205
        $lastLine = $tokens[$start]['line'];
206
        for ($i = ($start + 1); $i < $end; $i++) {
207
            if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
208
                continue;
209
            }
210
 
211
            $line = $tokens[$i]['line'];
212
            if ($line > $lastLine) {
213
                $type    = substr($tokens[$stackPtr]['type'], 2);
214
                $warning = "Code after $type statement cannot be executed";
215
                $phpcsFile->addWarning($warning, $i);
216
                $lastLine = $line;
217
            }
218
        }
219
 
220
    }//end process()
221
 
222
 
223
}//end class
224
 
225
?>