3
* PSR1_Sniffs_Files_SideEffectsSniff.
8
* @package PHP_CodeSniffer
9
* @author Greg Sherwood <gsherwood@squiz.net>
10
* @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
11
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
12
* @link http://pear.php.net/package/PHP_CodeSniffer
16
* PSR1_Sniffs_Files_SideEffectsSniff.
18
* Ensures a file declare new symbols and causes no other side effects, or executes
19
* logic with side effects, but not both.
22
* @package PHP_CodeSniffer
23
* @author Greg Sherwood <gsherwood@squiz.net>
24
* @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
25
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
26
* @version Release: 1.5.4
27
* @link http://pear.php.net/package/PHP_CodeSniffer
29
class PSR1_Sniffs_Files_SideEffectsSniff implements PHP_CodeSniffer_Sniff
33
* Returns an array of tokens this test wants to listen for.
37
public function register()
39
return array(T_OPEN_TAG);
45
* Processes this sniff, when one of its tokens is encountered.
47
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
48
* @param int $stackPtr The position of the current token in
53
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
55
// We are only interested if this is the first open tag.
56
if ($stackPtr !== 0) {
57
if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
62
$tokens = $phpcsFile->getTokens();
63
$result = $this->_searchForConflict($phpcsFile, 0, ($phpcsFile->numTokens - 1), $tokens);
65
if ($result['symbol'] !== null && $result['effect'] !== null) {
66
$error = 'A file should declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it should execute logic with side effects, but should not do both. The first symbol is defined on line %s and the first side effect is on line %s.';
68
$tokens[$result['symbol']]['line'],
69
$tokens[$result['effect']]['line'],
71
$phpcsFile->addWarning($error, 0, 'FoundWithSymbols', $data);
78
* Searches for symbol declarations and side effects.
80
* Returns the positions of both the first symbol declared and the first
81
* side effect in the file. A NULL value for either indicates nothing was
84
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
85
* @param int $start The token to start searching from.
86
* @param int $end The token to search to.
87
* @param array $tokens The stack of tokens that make up
92
private function _searchForConflict(PHP_CodeSniffer_File $phpcsFile, $start, $end, $tokens)
109
for ($i = $start; $i <= $end; $i++) {
110
// Ignore whitespace and comments.
111
if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
116
if ($tokens[$i]['code'] === T_OPEN_TAG
117
|| $tokens[$i]['code'] === T_CLOSE_TAG
122
// Ignore entire namespace, const and use statements.
123
if ($tokens[$i]['code'] === T_NAMESPACE) {
124
$next = $phpcsFile->findNext(array(T_SEMICOLON, T_OPEN_CURLY_BRACKET), ($i + 1));
125
if ($next === false) {
127
} else if ($tokens[$next]['code'] === T_OPEN_CURLY_BRACKET) {
128
$next = $tokens[$next]['bracket_closer'];
133
} else if ($tokens[$i]['code'] === T_USE
134
|| $tokens[$i]['code'] === T_CONST
136
$semicolon = $phpcsFile->findNext(T_SEMICOLON, ($i + 1));
137
if ($semicolon !== false) {
144
// Ignore function/class prefixes.
145
if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$methodPrefixes) === true) {
149
// Detect and skip over symbols.
150
if (in_array($tokens[$i]['code'], $symbols) === true
151
&& isset($tokens[$i]['scope_closer']) === true
153
if ($firstSymbol === null) {
157
$i = $tokens[$i]['scope_closer'];
159
} else if ($tokens[$i]['code'] === T_STRING
160
&& strtolower($tokens[$i]['content']) === 'define'
162
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($i - 1), null, true);
163
if ($tokens[$prev]['code'] !== T_OBJECT_OPERATOR) {
164
if ($firstSymbol === null) {
168
$i = $phpcsFile->findNext(T_SEMICOLON, ($i + 1));
173
// Conditional statements are allowed in symbol files as long as the
174
// contents is only a symbol definition. So don't count these as effects
176
if (in_array($tokens[$i]['code'], $conditions) === true) {
177
if (isset($tokens[$i]['scope_opener']) === false) {
178
// Probably an "else if", so just ignore.
182
$result = $this->_searchForConflict(
184
($tokens[$i]['scope_opener'] + 1),
185
($tokens[$i]['scope_closer'] - 1),
189
if ($result['symbol'] !== null) {
190
if ($firstSymbol === null) {
191
$firstSymbol = $result['symbol'];
194
if ($result['effect'] !== null) {
196
$firstEffect = $result['effect'];
201
if ($firstEffect === null) {
202
$firstEffect = $result['effect'];
205
$i = $tokens[$i]['scope_closer'];
209
if ($firstEffect === null) {
213
if ($firstSymbol !== null) {
214
// We have a conflict we have to report, so no point continuing.
220
'symbol' => $firstSymbol,
221
'effect' => $firstEffect,
224
}//end _searchForConflict()