Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
primogenial
/
themes
/
storefront
/
vendor
/
squizlabs
/
php_codesniffer
/
src
/
Standards
/
Squiz
/
Sniffs
/
PHP
:
NonExecutableCodeSniff.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php /** * Warns about code that can never been executed. * * @author Greg Sherwood <gsherwood@squiz.net> * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence */ namespace PHP_CodeSniffer\Standards\Squiz\Sniffs\PHP; use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Sniffs\Sniff; use PHP_CodeSniffer\Util\Tokens; class NonExecutableCodeSniff implements Sniff { /** * Returns an array of tokens this test wants to listen for. * * @return array */ public function register() { return [ T_BREAK, T_CONTINUE, T_RETURN, T_THROW, T_EXIT, T_GOTO, ]; }//end register() /** * Processes this test, when one of its tokens is encountered. * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. * * @return void */ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // If this token is preceded with an "or", it only relates to one line // and should be ignored. For example: fopen() or die(). $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); if ($tokens[$prev]['code'] === T_LOGICAL_OR || $tokens[$prev]['code'] === T_BOOLEAN_OR) { return; } // Check if this token is actually part of a one-line IF or ELSE statement. for ($i = ($stackPtr - 1); $i > 0; $i--) { if ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS) { $i = $tokens[$i]['parenthesis_opener']; continue; } else if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) { continue; } break; } if ($tokens[$i]['code'] === T_IF || $tokens[$i]['code'] === T_ELSE || $tokens[$i]['code'] === T_ELSEIF ) { return; } if ($tokens[$stackPtr]['code'] === T_RETURN) { $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); if ($tokens[$next]['code'] === T_SEMICOLON) { $next = $phpcsFile->findNext(T_WHITESPACE, ($next + 1), null, true); if ($tokens[$next]['code'] === T_CLOSE_CURLY_BRACKET) { // If this is the closing brace of a function // then this return statement doesn't return anything // and is not required anyway. $owner = $tokens[$next]['scope_condition']; if ($tokens[$owner]['code'] === T_FUNCTION) { $warning = 'Empty return statement not required here'; $phpcsFile->addWarning($warning, $stackPtr, 'ReturnNotRequired'); return; } } } } if (isset($tokens[$stackPtr]['scope_opener']) === true) { $owner = $tokens[$stackPtr]['scope_condition']; if ($tokens[$owner]['code'] === T_CASE || $tokens[$owner]['code'] === T_DEFAULT) { // This token closes the scope of a CASE or DEFAULT statement // so any code between this statement and the next CASE, DEFAULT or // end of SWITCH token will not be executable. $end = $phpcsFile->findEndOfStatement($stackPtr); $next = $phpcsFile->findNext( [ T_CASE, T_DEFAULT, T_CLOSE_CURLY_BRACKET, T_ENDSWITCH, ], ($end + 1) ); if ($next !== false) { $lastLine = $tokens[$end]['line']; for ($i = ($stackPtr + 1); $i < $next; $i++) { if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) { continue; } $line = $tokens[$i]['line']; if ($line > $lastLine) { $type = substr($tokens[$stackPtr]['type'], 2); $warning = 'Code after the %s statement on line %s cannot be executed'; $data = [ $type, $tokens[$stackPtr]['line'], ]; $phpcsFile->addWarning($warning, $i, 'Unreachable', $data); $lastLine = $line; } } }//end if // That's all we have to check for these types of statements. return; }//end if }//end if // This token may be part of an inline condition. // If we find a closing parenthesis that belongs to a condition // we should ignore this token. $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); if (isset($tokens[$prev]['parenthesis_owner']) === true) { $owner = $tokens[$prev]['parenthesis_owner']; $ignore = [ T_IF => true, T_ELSE => true, T_ELSEIF => true, ]; if (isset($ignore[$tokens[$owner]['code']]) === true) { return; } } $ourConditions = array_keys($tokens[$stackPtr]['conditions']); if (empty($ourConditions) === false) { $condition = array_pop($ourConditions); if (isset($tokens[$condition]['scope_closer']) === false) { return; } // Special case for BREAK statements sitting directly inside SWITCH // statements. If we get to this point, we know the BREAK is not being // used to close a CASE statement, so it is most likely non-executable // code itself (as is the case when you put return; break; at the end of // a case). So we need to ignore this token. if ($tokens[$condition]['code'] === T_SWITCH && $tokens[$stackPtr]['code'] === T_BREAK ) { return; } $closer = $tokens[$condition]['scope_closer']; // If the closer for our condition is shared with other openers, // we will need to throw errors from this token to the next // shared opener (if there is one), not to the scope closer. $nextOpener = null; for ($i = ($stackPtr + 1); $i < $closer; $i++) { if (isset($tokens[$i]['scope_closer']) === true) { if ($tokens[$i]['scope_closer'] === $closer) { // We found an opener that shares the same // closing token as us. $nextOpener = $i; break; } } }//end for if ($nextOpener === null) { $end = $closer; } else { $end = ($nextOpener - 1); } } else { // This token is in the global scope. if ($tokens[$stackPtr]['code'] === T_BREAK) { return; } // Throw an error for all lines until the end of the file. $end = ($phpcsFile->numTokens - 1); }//end if // Find the semicolon that ends this statement, skipping // nested statements like FOR loops and closures. for ($start = ($stackPtr + 1); $start < $phpcsFile->numTokens; $start++) { if ($start === $end) { break; } if (isset($tokens[$start]['parenthesis_closer']) === true && $tokens[$start]['code'] === T_OPEN_PARENTHESIS ) { $start = $tokens[$start]['parenthesis_closer']; continue; } if (isset($tokens[$start]['bracket_closer']) === true && $tokens[$start]['code'] === T_OPEN_CURLY_BRACKET ) { $start = $tokens[$start]['bracket_closer']; continue; } if ($tokens[$start]['code'] === T_SEMICOLON) { break; } }//end for if (isset($tokens[$start]) === false) { return; } $lastLine = $tokens[$start]['line']; for ($i = ($start + 1); $i < $end; $i++) { if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true || isset(Tokens::$bracketTokens[$tokens[$i]['code']]) === true || $tokens[$i]['code'] === T_SEMICOLON ) { continue; } // Skip whole functions and classes/interfaces because they are not // technically executed code, but rather declarations that may be used. if (isset(Tokens::$ooScopeTokens[$tokens[$i]['code']]) === true || $tokens[$i]['code'] === T_FUNCTION || $tokens[$i]['code'] === T_CLOSURE ) { if (isset($tokens[$i]['scope_closer']) === false) { // Parse error/Live coding. return; } $i = $tokens[$i]['scope_closer']; continue; } $line = $tokens[$i]['line']; if ($line > $lastLine) { $type = substr($tokens[$stackPtr]['type'], 2); $warning = 'Code after the %s statement on line %s cannot be executed'; $data = [ $type, $tokens[$stackPtr]['line'], ]; $phpcsFile->addWarning($warning, $i, 'Unreachable', $data); $lastLine = $line; } }//end for }//end process() }//end class