If a value is specified, the previous token of the specified type(s)
containing the specified value will be returned.
Returns false if no token can be found.
public findPrevious ( integer | array $types, integer $start, integer $end = null, boolean $exclude = false, string $value = null, boolean $local = false ) : integer | boolean | ||
$types | integer | array | The type(s) of tokens to search for. |
$start | integer | The position to start searching from in the token stack. |
$end | integer | The end position to fail if no token is found. if not specified or null, end will default to the start of the token stack. |
$exclude | boolean | If true, find the previous token that are NOT of the types specified in $types. |
$value | string | The value that the token(s) must be equal to. If value is omitted, tokens with any value will be returned. |
$local | boolean | If true, tokens outside the current statement will not be checked. IE. checking will stop at the previous semi-colon found. |
return | integer | boolean |
public function process(CodeSnifferFile $file, $stackPtr) { $tokens = $file->getTokens(); $fileName = $file->getFilename(); // Collect use statement aliases if ($tokens[$stackPtr]['code'] === T_USE) { /** function () use ($var) {} */ $previousPtr = $file->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); if ($tokens[$previousPtr]['code'] === T_CLOSE_PARENTHESIS) { return; } /** use Trait; */ if ($file->findPrevious([T_CLASS, T_TRAIT], $stackPtr)) { return; } list($stackPtr, $namespaceAlias, $fullyQualifiedNamespace) = $this->getNamespace($stackPtr + 1, $file); if (!isset(static::$aliases[$fileName])) { static::$aliases[$fileName] = []; } static::$aliases[$fileName][] = $namespaceAlias; return; } // Check if aliased exist for caught exceptions $catchPtr = $tokens[$stackPtr]['parenthesis_opener'] + 1; $exceptionPtr = $file->findNext([T_CLASS, T_INTERFACE], $catchPtr, $tokens[$stackPtr]['parenthesis_closer'], true); $exceptionName = $tokens[$exceptionPtr]['content']; if (!in_array($exceptionName, static::$aliases[$fileName], false)) { $file->addError(sprintf('Trying to catch an undefined exception. Please add use-statement for "%s"', $exceptionName), $exceptionPtr); } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile All the tokens found in the * document * @param int $stackPtr Position of the current token in the stack passed * in $tokens * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); switch ($tokens[$stackPtr]['type']) { case 'T_BOOLEAN_AND': case 'T_BOOLEAN_OR': $error = 'Operators && and || are not allowed, use AND and OR instead'; $phpcsFile->addError($error, $stackPtr); break; default: $beforePtr = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); $afterPtr = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); if ($tokens[$afterPtr]['type'] == 'T_VARIABLE') { switch ($tokens[$beforePtr]['type']) { case 'T_STRING': $beforePtr = $phpcsFile->findPrevious(T_WHITESPACE, $beforePtr - 1, null, true); if ($tokens[$beforePtr]['type'] == 'T_OBJECT_OPERATOR') { break; } case 'T_FALSE': case 'T_TRUE': case 'T_NULL': $error = 'Variables should precede constants in comparison operations'; $phpcsFile->addError($error, $stackPtr); break; } } break; } }
/** * Processes this sniff, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if (substr($tokens[$stackPtr]['content'], 0, 2) !== '//') { return; } $commentLine = $tokens[$stackPtr]['line']; $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); if ($tokens[$lastContent]['line'] !== $commentLine) { return; } if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) { return; } // Special case for JS files. if ($tokens[$lastContent]['code'] === T_COMMA || $tokens[$lastContent]['code'] === T_SEMICOLON) { $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, $lastContent - 1, null, true); if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) { return; } } $error = 'Comments may not appear after statements'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found'); if ($fix === true) { $phpcsFile->fixer->addNewlineBefore($stackPtr); } }
/** * Processes this sniff, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $next = $phpcsFile->findNext(T_INLINE_HTML, $stackPtr + 1, null, true); if ($next !== false) { return; } $has_html = $phpcsFile->findPrevious(T_INLINE_HTML, $stackPtr - 1); $prev = $phpcsFile->findPrevious(T_OPEN_TAG, $stackPtr - 1); if ($has_html !== false && $prev !== false && $tokens[$prev]['line'] === $tokens[$stackPtr]['line']) { // allow closing tag for single line PHP blocks in HTML files return; } // We've found the last closing tag in the file so the only thing // potentially remaining is inline HTML. Now we need to figure out // whether or not it's just a bunch of whitespace. $content = ''; for ($i = $stackPtr + 1; $i < $phpcsFile->numTokens; $i++) { $content .= $tokens[$i]['content']; } // Check if the remaining inline HTML is just whitespace. $content = trim($content); if (empty($content)) { $error = 'A closing tag is not permitted at the end of a PHP file'; $phpcsFile->addError($error, $stackPtr, 'NotAllowed'); } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $prev = $phpcsFile->findPrevious(T_SEMICOLON, ($stackPtr - 1)); if ($prev === false) { return; } // Ignore multiple statements in a FOR condition. if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) { foreach ($tokens[$stackPtr]['nested_parenthesis'] as $bracket) { $owner = $tokens[$bracket]['parenthesis_owner']; if ($tokens[$owner]['code'] === T_FOR) { return; } } } if ($tokens[$prev]['line'] === $tokens[$stackPtr]['line']) { $prevOpen = $phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)); $nextEnd = $phpcsFile->findNext(T_CLOSE_TAG, ($stackPtr + 1)); if ($tokens[$prevOpen]['line'] === $tokens[$stackPtr]['line'] && $tokens[$nextEnd]['line'] === $tokens[$stackPtr]['line']) { return; // Ignore if it is in a template (open and close tag in the same line) } else { $error = 'Each PHP statement must be on a line by itself'; $phpcsFile->addWarning($error, $stackPtr); return; } } }//end process()
/** * Process the sniff. Will be engaged when one of the tokens from ::register() is encountered. * * @param PHP_CodeSniffer_File $phpcsFile An instance of the current source file being scanned. * @param int $stackPtr The position of the encountered token in the provided file. * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // Nothing to do if the file is already in the global namespace. if ($phpcsFile->findPrevious(T_NAMESPACE, $stackPtr) === false) { return; } // Only proceed with checking the matched namespaced string if is not part of a `namespace Vendor\Foo;` or a `use Vendor\Bar as Baz;` statement. if ($phpcsFile->findPrevious([T_NAMESPACE, T_USE], $stackPtr - 1, null, false, null, true) === false) { $nextNonClassSegment = $phpcsFile->findNext([T_NS_SEPARATOR, T_STRING], $stackPtr + 1, null, true); $lastNsSeperator = $phpcsFile->findPrevious(T_NS_SEPARATOR, $nextNonClassSegment); // Only report for the last backslash matched in a single namespace string. (This sniff will trigger on each slash from `new \Some\Vendor\Lib();`, so this makes sure we don't report 3 errors for that same statement.) if ($lastNsSeperator === $stackPtr) { $start = $phpcsFile->findPrevious([T_NS_SEPARATOR, T_STRING], $stackPtr - 1, null, true) + 1; $end = $phpcsFile->findNext([T_NS_SEPARATOR, T_STRING], $start + 1, null, true); $class = ''; for ($i = $start; $i < $end; $i++) { $class .= $tokens[$i]['content']; } $tClass = $phpcsFile->findPrevious(T_CLASS, $stackPtr - 1); // Check if the code is attempting to extend a class with the same name. if ($tClass !== false) { $newClass = $phpcsFile->findNext(T_STRING, $tClass); if ($tokens[$newClass]['content'] == $tokens[$end - 1]['content']) { return; } $err = 'Namespaced class (%s) must be imported before use.'; $data = [$class]; $phpcsFile->addError($err, $stackPtr, 'ClassMustBeImported', $data); } } } }
/** * Processes the function tokens within the class. * * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found. * @param int $stackPtr The position where the token was found. * @param int $currScope The current scope opener token. * * @return void */ protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope) { $tokens = $phpcsFile->getTokens(); $methodName = $phpcsFile->getDeclarationName($stackPtr); if ($methodName === null) { // Ignore closures. return; } $modifier = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$scopeModifiers, $stackPtr); if ($modifier === false || $tokens[$modifier]['line'] !== $tokens[$stackPtr]['line']) { $error = 'Visibility must be declared on method "%s"'; $data = array($methodName); $previous = $phpcsFile->findPrevious(array(T_WHITESPACE), $stackPtr - 1, null, true); // Only correct the trivial cases for now if (!$modifier && $tokens[$modifier]['line'] === $tokens[$stackPtr]['line']) { $phpcsFile->addFixableError($error, $stackPtr, 'Missing', $data); $visbility = 'public'; $name = substr($tokens[$stackPtr]['content'], 1); $totalUnderscores = 0; while (strpos($name, '_') === 0) { $totalUnderscores++; $name = substr($name, 1); } if ($totalUnderscores > 1) { $visbility = 'private'; } elseif ($totalUnderscores > 0) { $visbility = 'protected'; } } else { $phpcsFile->addError($error, $stackPtr, 'Missing', $data); } //TODO } }
/** * @param \PHP_CodeSniffer_File $phpcsFile * @param int $stackPtr */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $find = PHP_CodeSniffer_Tokens::$methodPrefixes; $find[] = T_WHITESPACE; $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1, null, true); if ($tokens[$commentEnd]['code'] === T_COMMENT) { // Inline comments might just be closing comments for // control structures or functions instead of function comments // using the wrong comment type. If there is other code on the line, // assume they relate to that code. $prev = $phpcsFile->findPrevious($find, $commentEnd - 1, null, true); if ($prev !== false && $tokens[$prev]['line'] === $tokens[$commentEnd]['line']) { $commentEnd = $prev; } } if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG && $tokens[$commentEnd]['code'] !== T_COMMENT) { if (array_key_exists('scope_opener', $tokens[$stackPtr]) && $this->functionHasReturn($phpcsFile, $stackPtr)) { $phpcsFile->addError('Function has return keyword but no doc block', $stackPtr); return; } if ($this->functionHasParams($phpcsFile, $stackPtr)) { $phpcsFile->addError('Function has parameters but no doc block', $stackPtr); } } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $prev = $phpcsFile->findPrevious(T_SEMICOLON, $stackPtr - 1); if ($prev === false) { return; } // Ignore multiple statements in a FOR condition. if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) { foreach ($tokens[$stackPtr]['nested_parenthesis'] as $bracket) { if (isset($tokens[$bracket]['parenthesis_owner']) === false) { // Probably a closure sitting inside a function call. continue; } $owner = $tokens[$bracket]['parenthesis_owner']; if ($tokens[$owner]['code'] === T_FOR) { return; } } } if ($tokens[$prev]['line'] === $tokens[$stackPtr]['line']) { $inline_html = $phpcsFile->findPrevious(T_INLINE_HTML, $stackPtr - 1); if ($inline_html !== false && $tokens[$inline_html]['line'] === $tokens[$stackPtr]['line']) { return; } $error = 'Each PHP statement must be on a line by itself'; $phpcsFile->addError($error, $stackPtr, 'SameLine'); return; } }
/** * Processes the function tokens within the class. * * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found. * @param int $stackPtr The position where the token was found. * * @return void */ protected function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $modifier = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$scopeModifiers, $stackPtr); if ($modifier === false || $tokens[$modifier]['line'] !== $tokens[$stackPtr]['line']) { $error = 'Scope modifier not specified for member variable "%s"'; $previous = $phpcsFile->findPrevious(array(T_WHITESPACE), $stackPtr - 1, null, true); $data = array($tokens[$stackPtr]['content']); if ($previous && $tokens[$previous]['code'] === T_VAR) { $phpcsFile->addFixableError($error, $stackPtr, 'Missing', $data); if ($phpcsFile->fixer->enabled === true) { $visbility = 'public'; $name = substr($tokens[$stackPtr]['content'], 1); $totalUnderscores = 0; while (strpos($name, '_') === 0) { $totalUnderscores++; $name = substr($name, 1); } if ($totalUnderscores > 1) { $visbility = 'private'; } elseif ($totalUnderscores > 0) { $visbility = 'protected'; } $phpcsFile->fixer->replaceToken($previous, $visbility); } } else { $phpcsFile->addError($error, $stackPtr, 'Missing', $data); } } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $tokenizer = $phpcsFile->tokenizerType; $openBracket = $tokens[$stackPtr]['parenthesis_opener']; $closeBracket = $tokens[$stackPtr]['parenthesis_closer']; if ($tokens[$stackPtr]['code'] === T_FOR) { // We only want to check the condition in FOR loops. $start = $phpcsFile->findNext(T_SEMICOLON, $openBracket + 1); $end = $phpcsFile->findPrevious(T_SEMICOLON, $closeBracket - 1); } else { $start = $openBracket; $end = $closeBracket; } for ($i = $start + 1; $i < $end; $i++) { if ($tokens[$i]['code'] === T_STRING && in_array($tokens[$i]['content'], $this->forbiddenFunctions[$tokenizer])) { $functionName = $tokens[$i]['content']; if ($tokenizer === 'JS') { // Needs to be in the form object.function to be valid. $prev = $phpcsFile->findPrevious(T_WHITESPACE, $i - 1, null, true); if ($prev === false || $tokens[$prev]['code'] !== T_OBJECT_OPERATOR) { continue; } $functionName = 'object.' . $functionName; } else { $functionName .= '()'; } $error = 'The use of %s inside a loop condition is not allowed; assign the return value to a variable and use the variable in the loop condition instead'; $data = array($functionName); $phpcsFile->addError($error, $i, 'Found', $data); } //end if } //end for }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile * @param int $stackPtr */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $functionToken = $phpcsFile->findNext(T_FUNCTION, $stackPtr); if ($functionToken === false) { return; } $nameToken = $phpcsFile->findNext(T_STRING, $functionToken); if (in_array($tokens[$nameToken]['content'], $this->magicMethods) === false) { return; } $scopeToken = $phpcsFile->findPrevious(array(T_PUBLIC, T_PROTECTED, T_PRIVATE), $nameToken, $stackPtr); if ($scopeToken === false) { return; } if ($tokens[$scopeToken]['type'] != 'T_PUBLIC') { $error = "Magic methods must be public (since PHP 5.3) !"; $phpcsFile->addError($error, $stackPtr); } $staticToken = $phpcsFile->findPrevious(T_STATIC, $scopeToken, $scopeToken - 2); if ($staticToken === false) { return; } else { $error = "Magic methods can not be static (since PHP 5.3) !"; $phpcsFile->addError($error, $stackPtr); } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $utils = Security_Sniffs_UtilsFactory::getInstance(); $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr]['content'] == "'#value'" || $tokens[$stackPtr]['content'] == '"#value"') { $closer = $phpcsFile->findNext(T_SEMICOLON, $stackPtr); $next = $phpcsFile->findNext(array_merge(PHP_CodeSniffer_Tokens::$bracketTokens, PHP_CodeSniffer_Tokens::$emptyTokens, PHP_CodeSniffer_Tokens::$assignmentTokens), $stackPtr + 1, $closer + 1, true); if ($next == $closer && $tokens[$next]['code'] == T_SEMICOLON) { // Case of $label = $element['#value']; $next = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$assignmentTokens, $next); $next = $phpcsFile->findPrevious(T_VARIABLE, $next); $phpcsFile->addWarning('Potential XSS found with #value on ' . $tokens[$next]['content'], $next, 'D7XSSWarFormValue'); } elseif ($next && $utils::is_token_user_input($tokens[$next])) { $phpcsFile->addError('XSS found with #value on ' . $tokens[$next]['content'], $next, 'D7XSSErrFormValue'); } elseif ($next && PHP_CodeSniffer::getConfigData('ParanoiaMode')) { if (in_array($tokens[$next]['content'], $utils::getXSSMitigationFunctions())) { $n = $phpcsFile->findNext($utils::getVariableTokens(), $next + 1, $closer); if ($n) { $phpcsFile->addWarning('Potential XSS found with #value on ' . $tokens[$n]['content'], $n, 'D7XSSWarFormValue'); } } else { $phpcsFile->addWarning('Potential XSS found with #value on ' . $tokens[$next]['content'], $next, 'D7XSSWarFormValue'); } } } }
/** * @inheritdoc */ public function process(\PHP_CodeSniffer_File $phpCsFile, $stackPointer) { $tokens = $phpCsFile->getTokens(); $prevIndex = $phpCsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPointer - 1, null, true); if (!in_array($tokens[$prevIndex]['code'], [T_TRUE, T_FALSE, T_NULL, T_LNUMBER, T_CONSTANT_ENCAPSED_STRING])) { return; } $leftIndexEnd = $prevIndex; $leftIndexStart = $prevIndex; $prevIndex = $phpCsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $prevIndex - 1, null, true); if (!$prevIndex) { return; } if ($this->isGivenKind(PHP_CodeSniffer_Tokens::$arithmeticTokens, $tokens[$prevIndex])) { return; } if ($this->isGivenKind([T_STRING_CONCAT], $tokens[$prevIndex])) { return; } $fixable = true; $error = 'Usage of Yoda conditions is not allowed. Switch the expression order.'; $prevContent = $tokens[$prevIndex]['content']; if (!$this->isGivenKind(PHP_CodeSniffer_Tokens::$assignmentTokens, $tokens[$prevIndex]) && !$this->isGivenKind(PHP_CodeSniffer_Tokens::$booleanOperators, $tokens[$prevIndex]) && $prevContent !== '(') { // Not fixable $phpCsFile->addError($error, $stackPointer); return; } //TODO }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $token = $tokens[$stackPtr]; // exclude function definitions, class methods, and namespaced calls if ($token['code'] == T_STRING && ($prev = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true)) && (in_array($tokens[$prev]['code'], array(T_FUNCTION, T_DOUBLE_COLON, T_OBJECT_OPERATOR)) || $tokens[$prev]['code'] == T_NS_SEPARATOR && ($pprev = $phpcsFile->findPrevious(T_WHITESPACE, $prev - 1, null, true)) && $tokens[$pprev]['code'] == T_STRING)) { return; } $exclude = explode(',', $this->exclude); $groups = $this->getGroups(); if (empty($groups)) { return; } foreach ($groups as $groupName => $group) { if (in_array($groupName, $exclude)) { continue; } $functions = implode('|', $group['functions']); $functions = preg_replace('#[^\\.]\\*#', '.*', $functions); // So you can use * instead of .* if (preg_match('#\\b(' . $functions . ')\\b#', $token['content']) < 1) { continue; } if ($group['type'] == 'warning') { $addWhat = array($phpcsFile, 'addWarning'); } else { $addWhat = array($phpcsFile, 'addError'); } call_user_func($addWhat, $group['message'], $stackPtr, $groupName, array($token['content'])); } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $find = PHP_CodeSniffer_Tokens::$methodPrefixes; $find[] = T_WHITESPACE; // Get class name $className = $tokens[$stackPtr + 2]['content']; // Currently only run on migrations if (!preg_match("/Migration[^\\s]*/", $className)) { return; } $docTokens = array(T_COMMENT, T_DOC_COMMENT_STAR, T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_TAG, T_DOC_COMMENT_OPEN_TAG, T_DOC_COMMENT_CLOSE_TAG, T_DOC_COMMENT_STRING); $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1, null, true); if (!in_array($tokens[$commentEnd]['code'], $docTokens)) { $phpcsFile->addError('Missing migration class doc comment', $stackPtr, 'Missing'); return; } // Get doc block comment $commentLocation = $phpcsFile->findPrevious(T_DOC_COMMENT_STRING, $commentEnd); $commentString = $tokens[$commentLocation]['content']; // Make sure its not default from stub if (preg_match("/Migration\\sscript\\sfor\\s\\.\\.\\./", $commentString)) { $phpcsFile->addError('Please complete migration doc block comment.', $stackPtr, 'Missing'); return; } }
/** * Processes this function call. * * @param PHP_CodeSniffer_File $phpcsFile * The file being scanned. * @param int $stackPtr * The position of the function call in the stack. * @param int $openBracket * The position of the opening parenthesis in the stack. * @param int $closeBracket * The position of the closing parenthesis in the stack. * @param Drupal_Sniffs_Semantics_FunctionCallSniff $sniff * Can be used to retreive the function's arguments with the getArgument() * method. * * @return void */ public function processFunctionCall(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $openBracket, $closeBracket, Drupal_Sniffs_Semantics_FunctionCallSniff $sniff) { $tokens = $phpcsFile->getTokens(); // We assume that the sequence '#default_value' => variable_get(...) // indicates a variable that the module owns. $arrow = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr - 1, null, true); if ($arrow === false || $tokens[$arrow]['code'] !== T_DOUBLE_ARROW) { return; } $arrayKey = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $arrow - 1, null, true); if ($arrayKey === false || $tokens[$arrayKey]['code'] !== T_CONSTANT_ENCAPSED_STRING || substr($tokens[$arrayKey]['content'], 1, -1) !== '#default_value') { return; } $argument = $sniff->getArgument(1); // Variable name is not a literal string, so we return early. if ($argument === false || $tokens[$argument['start']]['code'] !== T_CONSTANT_ENCAPSED_STRING) { return; } $moduleName = DrupalPractice_Project::getName($phpcsFile); if ($moduleName === false) { return; } $variableName = substr($tokens[$argument['start']]['content'], 1, -1); if (strpos($variableName, $moduleName) !== 0) { $warning = 'All variables defined by your module must be prefixed with your module\'s name to avoid name collisions with others. Expected start with "%s" but found "%s"'; $data = array($moduleName, $variableName); $phpcsFile->addWarning($warning, $argument['start'], 'VariableName', $data); } }
/** * Processes normal variables. * * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found. * @param int $stackPtr The position where the token was found. * * @return void */ protected function processVariable(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $variableName = $tokens[$stackPtr]['content']; $scopeIdentifier = $phpcsFile->getFilename() . $variableName; $level = $tokens[$stackPtr]['level']; $functionIndex = $phpcsFile->findPrevious(T_FUNCTION, $stackPtr); $lastScopeOpen = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$scopeOpeners, $stackPtr); //Inline scope openers do not increment the level value $scopeOpenDistance = $tokens[$stackPtr]['line'] - $tokens[$lastScopeOpen]['line']; if (in_array($tokens[$lastScopeOpen]['code'], PHP_CodeSniffer_Tokens::$scopeOpeners) === true && ($scopeOpenDistance === 1 || $scopeOpenDistance === 0) && $tokens[$stackPtr]['level'] === $tokens[$lastScopeOpen]['level']) { ++$level; } if ($functionIndex !== false && array_key_exists('scope_closer', $tokens[$functionIndex]) && $tokens[$functionIndex]['scope_closer'] > $stackPtr) { //Member variables are always ok if ($variableName === '$this') { return; } // find previous non-whitespace token. if it's a double colon, assume static class var $objOperator = $phpcsFile->findPrevious([T_WHITESPACE], $stackPtr - 1, null, true); if ($tokens[$objOperator]['code'] === T_DOUBLE_COLON) { return; } $scopeIdentifier .= $tokens[$functionIndex]['scope_condition']; } //If this is the first time we've seen this variable in this file/function store the scope depth. if (array_key_exists($scopeIdentifier, $this->_variableScopes) === false) { $this->_variableScopes[$scopeIdentifier] = $level; } elseif ($this->_variableScopes[$scopeIdentifier] > $level) { //Verify that the variables we've seen are not appearing in higher scopes. $phpcsFile->addWarning("Variable '{$variableName}' is in the wrong scope.", $stackPtr, 'Found'); } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // Ignore this.something and other uses of "this" that are not // direct assignments. $next = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); if ($tokens[$next]['code'] !== T_SEMICOLON) { if ($tokens[$next]['line'] === $tokens[$stackPtr]['line']) { return; } } // Something must be assigned to "this". $prev = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); if ($tokens[$prev]['code'] !== T_EQUAL) { return; } // A variable needs to be assigned to "this". $prev = $phpcsFile->findPrevious(T_WHITESPACE, $prev - 1, null, true); if ($tokens[$prev]['code'] !== T_STRING) { return; } // We can only assign "this" to a var called "self". if ($tokens[$prev]['content'] !== 'self' && $tokens[$prev]['content'] !== '_self') { $error = 'Keyword "this" can only be assigned to a variable called "self" or "_self"'; $phpcsFile->addError($error, $prev, 'NotSelf'); } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $next = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); if ($next === false) { return; } if ($tokens[$next]['code'] !== T_CLOSE_TAG) { $found = $tokens[$next]['line'] - $tokens[$stackPtr]['line'] - 1; if ($found !== 1) { $error = 'Expected one blank line after closing brace of class definition; %s found'; $data = array($found); $phpcsFile->addError($error, $stackPtr, 'SpacingAfterClose', $data); } } // Ignore nested style definitions from here on. The spacing before the closing brace // (a single blank line) will be enforced by the above check, which ensures there is a // blank line after the last nested class. $found = $phpcsFile->findPrevious(T_CLOSE_CURLY_BRACKET, $stackPtr - 1, $tokens[$stackPtr]['bracket_opener']); if ($found !== false) { return; } $prev = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr - 1, null, true); if ($prev !== false && $tokens[$prev]['line'] !== $tokens[$stackPtr]['line'] - 1) { $num = $tokens[$stackPtr]['line'] - $tokens[$prev]['line'] - 1; $error = 'Expected 0 blank lines before closing brace of class definition; %s found'; $data = array($num); $phpcsFile->addError($error, $stackPtr, 'SpacingBeforeClose', $data); } }
/** * Processes the function tokens within the class. * * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found. * @param int $stackPtr The position where the token was found. * * @return void */ protected function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // Detect multiple properties defined at the same time. Throw an error // for this, but also only process the first property in the list so we don't // repeat errors. $find = PHP_CodeSniffer_Tokens::$scopeModifiers; $find = array_merge($find, array(T_VARIABLE, T_VAR, T_SEMICOLON)); $prev = $phpcsFile->findPrevious($find, $stackPtr - 1); if ($tokens[$prev]['code'] === T_VARIABLE) { return; } if ($tokens[$prev]['code'] === T_VAR) { $error = 'The var keyword must not be used to declare a property'; $phpcsFile->addError($error, $stackPtr, 'VarUsed'); } $next = $phpcsFile->findNext(array(T_VARIABLE, T_SEMICOLON), $stackPtr + 1); if ($tokens[$next]['code'] === T_VARIABLE) { $error = 'There must not be more than one property declared per statement'; $phpcsFile->addError($error, $stackPtr, 'Multiple'); } $modifier = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$scopeModifiers, $stackPtr); if ($modifier === false || $tokens[$modifier]['line'] !== $tokens[$stackPtr]['line']) { $error = 'Visibility must be declared on property "%s"'; $data = array($tokens[$stackPtr]['content']); $phpcsFile->addError($error, $stackPtr, 'ScopeMissing', $data); } $propertyName = ltrim($tokens[$stackPtr]['content'], '$'); if (PHP_CodeSniffer::isCamelCaps($propertyName, false, true, false) === false) { $error = 'Property name "%s" is not in camel caps format'; $errorData = array($tokens[$stackPtr]['content']); $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData); } }
/** * Processes the function tokens within the class. * * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found. * @param int $stackPtr The position where the token was found. * * @return void */ protected function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // if ($tokens[$stackPtr]['content'][1] === '_') { // $error = 'Property name "%s" should not be prefixed with an underscore to indicate visibility'; // $data = array($tokens[$stackPtr]['content']); // $phpcsFile->addWarning($error, $stackPtr, 'Underscore', $data); // } // Detect multiple properties defined at the same time. Throw an error // for this, but also only process the first property in the list so we don't // repeat errors. $find = PHP_CodeSniffer_Tokens::$scopeModifiers; $find = array_merge($find, array(T_VARIABLE, T_VAR, T_SEMICOLON)); $prev = $phpcsFile->findPrevious($find, $stackPtr - 1); if ($tokens[$prev]['code'] === T_VARIABLE) { return; } if ($tokens[$prev]['code'] === T_VAR) { $error = 'The var keyword must not be used to declare a property'; $phpcsFile->addError($error, $stackPtr, 'VarUsed'); } $next = $phpcsFile->findNext(array(T_VARIABLE, T_SEMICOLON), $stackPtr + 1); if ($tokens[$next]['code'] === T_VARIABLE) { $error = 'There must not be more than one property declared per statement'; $phpcsFile->addError($error, $stackPtr, 'Multiple'); } $modifier = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$scopeModifiers, $stackPtr); if ($modifier === false || $tokens[$modifier]['line'] !== $tokens[$stackPtr]['line']) { $error = 'Visibility must be declared on property "%s"'; $data = array($tokens[$stackPtr]['content']); $phpcsFile->addError($error, $stackPtr, 'ScopeMissing', $data); } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile All the tokens found in the document. * @param int $stackPtr The position of the current token in * the stack passed in $tokens. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $current = $stackPtr; $openingBrace = $phpcsFile->findNext([T_OPEN_CURLY_BRACKET], $current, null); $firstMethodComment = $phpcsFile->findNext([T_DOC_COMMENT_OPEN_TAG], $openingBrace); if ($tokens[$openingBrace]['line'] > $tokens[$firstMethodComment]['line']) { return; } $elementToCheck = $firstMethodComment; $trait = $phpcsFile->findPrevious(T_USE, $firstMethodComment); if (true === is_int($trait)) { $elementToCheck = $trait; } $constant = $phpcsFile->findNext([T_CONST], $openingBrace); $use = $phpcsFile->findNext([T_USE], $openingBrace, $firstMethodComment); if (false !== $constant && $constant > $openingBrace && $constant < $firstMethodComment) { $elementBeforeConstant = $phpcsFile->findPrevious([T_WHITESPACE], $openingBrace, $constant, true); if (false != $use && $tokens[$openingBrace]['line'] + 1 !== $tokens[$use]['line']) { $phpcsFile->addError('Remove blank line before use Statement', $use); } if (false === $use && false === $elementBeforeConstant && $tokens[$openingBrace]['line'] + 1 != $tokens[$constant]['line']) { $phpcsFile->addError('Remove blank line before const Statement', $constant); } if ($use != false && $tokens[$phpcsFile->findPrevious([T_USE], $constant, $openingBrace)]['line'] + 2 !== $tokens[$constant]['line']) { $phpcsFile->addError('Only one blank line before constant statement is allowed', $constant); } return; } if (1 < $tokens[$elementToCheck]['line'] - $tokens[$openingBrace]['line']) { $phpcsFile->addError('Remove blank line before.', $elementToCheck); } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr]['code'] === T_CASE && isset($tokens[$stackPtr]['scope_closer'])) { // $internalCase = $phpcsFile->findNext(T_CASE, $stackPtr + 1, $tokens[$stackPtr]['scope_closer']); // if ($internalCase !== false) { // $comment = $phpcsFile->findNext(T_COMMENT, $stackPtr + 1, $internalCase - 1); // if ($comment === false) { // $phpcsFile->addError($this->getReqPrefix('REQ.PHP.2.5.12') . '"case" has not break and has not any comment', $stackPtr); // } // } $switch = $phpcsFile->findPrevious(T_SWITCH, $stackPtr - 1); if ($switch !== false) { $nextCase = $phpcsFile->findNext(array(T_CASE, T_DEFAULT), $stackPtr + 1, $tokens[$switch]['scope_closer']); if ($nextCase !== false) { $prevBreak = $phpcsFile->findPrevious(T_BREAK, $nextCase - 1, $stackPtr); if ($prevBreak !== false) { $breakWS = $phpcsFile->findNext(T_WHITESPACE, $prevBreak + 1, $nextCase - 1); if ($breakWS !== false) { $str = $phpcsFile->getTokensAsString($breakWS, $nextCase - $breakWS - 1); if (!preg_match("/^\n\n[ ]*\$/Ss", $str)) { $breakWS = false; } } if ($breakWS === false) { $phpcsFile->addError($this->getReqPrefix('REQ.PHP.2.5.14') . '"case" must has empty line between current "case" and previous "break"', $stackPtr); } } } } } elseif ($tokens[$stackPtr]['code'] === T_DEFAULT) { } }
/** * Processes this test, when one of its tokens is encountered. * * Operations to check for: * $i = 1 + 1; * $i = $i + 1; * $i = (1 + 1) - 1; * * Operations to ignore: * array($i => -1); * $i = -1; * range(-10, -1); * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $has_equality_token = in_array($tokens[$stackPtr - 2]['code'], PHP_CodeSniffer_Tokens::$equalityTokens); // Ensure this is a construct to check. $lastSyntaxItem = $phpcsFile->findPrevious(array(T_WHITESPACE), $stackPtr - 1, $tokens[$stackPtr]['column'] * -1, true, NULL, true); $needs_operator_suffix = in_array($tokens[$lastSyntaxItem]['code'], array(T_LNUMBER, T_DNUMBER, T_CLOSE_PARENTHESIS, T_CLOSE_SQUARE_BRACKET, T_CLOSE_CURLY_BRACKET, T_VARIABLE, T_STRING, T_CONSTANT_ENCAPSED_STRING)); $needs_operator_prefix = !in_array($tokens[$lastSyntaxItem]['code'], array(T_OPEN_PARENTHESIS, T_EQUAL)); if ($needs_operator_suffix && ($tokens[$stackPtr - 2]['code'] !== T_EQUAL && $tokens[$stackPtr - 2]['code'] !== T_DOUBLE_ARROW && !$has_equality_token && !($tokens[$stackPtr]['code'] === T_EQUAL && $tokens[$stackPtr + 1]['code'] === T_BITWISE_AND) && ($tokens[$stackPtr + 1]['code'] !== T_WHITESPACE || $tokens[$stackPtr + 1]['content'] != ' '))) { $error = 'An operator statement must be followed by a single space'; $phpcsFile->addError($error, $stackPtr); } if ($needs_operator_prefix) { $error = false; if ($tokens[$stackPtr - 1]['code'] !== T_WHITESPACE) { $error = true; } else { if ($tokens[$stackPtr - 1]['content'] !== ' ' && $tokens[$stackPtr]['code'] !== T_EQUAL) { $nonWhiteSpace = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr - 1, null, true); // Make sure that the previous operand is on the same line before // throwing an error. if ($tokens[$nonWhiteSpace]['line'] === $tokens[$stackPtr]['line']) { $error = true; } } } if ($error === true) { $error = 'There must be a single space before an operator statement'; $phpcsFile->addError($error, $stackPtr); } } //end if }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // We are only interested in function/class/interface doc block comments. $nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true); $ignore = array(T_CLASS, T_INTERFACE, T_FUNCTION, T_PUBLIC, T_PRIVATE, T_PROTECTED, T_STATIC, T_ABSTRACT); if (in_array($tokens[$nextToken]['code'], $ignore) === false) { // Could be a file comment. $prevToken = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr - 1, null, true); if ($tokens[$prevToken]['code'] !== T_OPEN_TAG) { return; } } // We only want to get the first comment in a block. If there is // a comment on the line before this one, return. $docComment = $phpcsFile->findPrevious(T_DOC_COMMENT, $stackPtr - 1); if ($docComment !== false) { if ($tokens[$docComment]['line'] === $tokens[$stackPtr]['line'] - 1) { return; } } $comments = array($stackPtr); $currentComment = $stackPtr; $lastComment = $stackPtr; while (($currentComment = $phpcsFile->findNext(T_DOC_COMMENT, $currentComment + 1)) !== false) { if ($tokens[$lastComment]['line'] === $tokens[$currentComment]['line'] - 1) { $comments[] = $currentComment; $lastComment = $currentComment; } else { break; } } // The $comments array now contains pointers to each token in the // comment block. $requiredColumn = strpos($tokens[$stackPtr]['content'], '*'); $requiredColumn += $tokens[$stackPtr]['column']; foreach ($comments as $commentPointer) { // Check the spacing after each asterisk. $content = $tokens[$commentPointer]['content']; $firstChar = substr($content, 0, 1); $lastChar = substr($content, -1); if ($firstChar === '/' || $lastChar !== '/') { continue; } $count = substr_count($content, '*'); if ($count < 2) { continue; } $error = 'Expected 1 asterisk on closing line; %s found'; $data = array($count); $fix = $phpcsFile->addFixableError($error, $commentPointer, 'SpaceBeforeTag', $data); if ($fix === true && $phpcsFile->fixer->enabled === true) { $pos = strpos($content, '*'); $content = substr($content, 0, $pos + 1) . substr($content, $pos + $count); $phpcsFile->fixer->replaceToken($commentPointer, $content); } } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $find = PHP_CodeSniffer_Tokens::$methodPrefixes; $find[] = T_WHITESPACE; $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1, null, true); if ($tokens[$commentEnd]['code'] === T_COMMENT) { // Inline comments might just be closing comments for // control structures or functions instead of function comments // using the wrong comment type. If there is other code on the line, // assume they relate to that code. $prev = $phpcsFile->findPrevious($find, $commentEnd - 1, null, true); if ($prev !== false && $tokens[$prev]['line'] === $tokens[$commentEnd]['line']) { $commentEnd = $prev; } } if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG && $tokens[$commentEnd]['code'] !== T_COMMENT) { $phpcsFile->addError('Missing function doc comment', $stackPtr, 'Missing'); $phpcsFile->recordMetric($stackPtr, 'Function has doc comment', 'no'); return; } else { $phpcsFile->recordMetric($stackPtr, 'Function has doc comment', 'yes'); } if ($tokens[$commentEnd]['code'] === T_COMMENT) { $phpcsFile->addError('You must use "/**" style comments for a function comment', $stackPtr, 'WrongStyle'); return; } if ($tokens[$commentEnd]['line'] !== $tokens[$stackPtr]['line'] - 1) { $error = 'There must be no blank lines after the function comment'; $phpcsFile->addError($error, $commentEnd, 'SpacingAfter'); } $commentStart = $tokens[$commentEnd]['comment_opener']; foreach ($tokens[$commentStart]['comment_tags'] as $tag) { if ($tokens[$tag]['content'] === '@see') { // Make sure the tag isn't empty. $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd); if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) { $error = 'Content missing for @see tag in function comment'; $phpcsFile->addError($error, $tag, 'EmptySees'); } } // Ongr checks inheritdoc comment. if ($tokens[$tag]['content'] === '@inheritdoc') { $error = 'You must use {@inheritdoc}'; $fix = $phpcsFile->addFixableError($error, $tag, 'WrongInheritDocStyle'); if ($fix === true) { $phpcsFile->fixer->replaceToken($tag, '{@inheritdoc}'); } return; } } $this->processReturn($phpcsFile, $stackPtr, $commentStart); $this->processThrows($phpcsFile, $stackPtr, $commentStart); $this->processDeprecated($phpcsFile, $stackPtr, $commentStart); $this->processParams($phpcsFile, $stackPtr, $commentStart); $this->processComments($phpcsFile, $stackPtr, $commentStart); }
/** * Processes this sniff, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // We are only interested in Action classes. $classNameToken = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); $className = $tokens[$classNameToken]['content']; if (substr($className, -7) !== 'Actions') { return; } $foundFunctions = array(); $foundCalls = array(); // Find all static method calls in the form self::method() in the class. $classEnd = $tokens[$stackPtr]['scope_closer']; for ($i = $classNameToken + 1; $i < $classEnd; $i++) { if ($tokens[$i]['code'] !== T_DOUBLE_COLON) { if ($tokens[$i]['code'] === T_FUNCTION) { // Cache the function information. $funcName = $phpcsFile->findNext(T_STRING, $i + 1); $funcScope = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$scopeModifiers, $i - 1); $foundFunctions[$tokens[$funcName]['content']] = strtolower($tokens[$funcScope]['content']); } continue; } $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, $i - 1, null, true); if ($tokens[$prevToken]['content'] !== 'self') { continue; } $funcNameToken = $phpcsFile->findNext(T_WHITESPACE, $i + 1, null, true); if ($tokens[$funcNameToken]['code'] === T_VARIABLE) { // We are only interested in function calls. continue; } $funcName = $tokens[$funcNameToken]['content']; // We've found the function, now we need to find it and see if it is // public, private or protected. If it starts with an underscore we // can assume it is private. if ($funcName[0] === '_') { continue; } $foundCalls[$i] = $funcName; } //end for $errorClassName = substr($className, 0, -7); foreach ($foundCalls as $token => $funcName) { if (isset($foundFunctions[$funcName]) === false) { // Function was not in this class, might have come from the parent. // Either way, we can't really check this. continue; } else { if ($foundFunctions[$funcName] === 'public') { $error = "Static calls to public methods in Action classes must not use the self keyword; use {$errorClassName}::{$funcName}() instead"; $phpcsFile->addError($error, $token); } } } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $find = PHP_CodeSniffer_Tokens::$methodPrefixes; $find[] = T_WHITESPACE; $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1, null, true); // If the token that we found was a class or a function, then this // function has no doc comment. $code = $tokens[$commentEnd]['code']; // REPLACED $this->_methodName = $phpcsFile->getDeclarationName($stackPtr); if ($code === T_COMMENT) { // Inline comments might just be closing comments for // control structures or functions instead of function comments // using the wrong comment type. If there is other code on the line, // assume they relate to that code. $prev = $phpcsFile->findPrevious($find, $commentEnd - 1, null, true); if ($prev !== false && $tokens[$prev]['line'] === $tokens[$commentEnd]['line']) { $commentEnd = $prev; } } if ($code !== T_DOC_COMMENT_CLOSE_TAG && $code !== T_COMMENT) { // EDITED: Constructor must not have comment if ($this->_methodName !== '__construct' && $this->_methodName !== '__destruct' && substr($this->_methodName, 0, 6) !== 'inject') { $phpcsFile->addError('Missing function doc comment', $stackPtr, 'Missing'); $phpcsFile->recordMetric($stackPtr, 'Function has doc comment', 'no'); } return; } else { $phpcsFile->recordMetric($stackPtr, 'Function has doc comment', 'yes'); } if ($code === T_COMMENT) { if ($this->_methodName !== '__construct' && $this->_methodName !== '__destruct' && substr($this->_methodName, 0, 6) !== 'inject') { $phpcsFile->addError('You must use "/**" style comments for a function comment', $stackPtr, 'WrongStyle'); } return; } if ($tokens[$commentEnd]['line'] !== $tokens[$stackPtr]['line'] - 1) { $error = 'There must be no blank lines after the function comment'; $phpcsFile->addError($error, $commentEnd, 'SpacingAfter'); } $commentStart = $tokens[$commentEnd]['comment_opener']; foreach ($tokens[$commentStart]['comment_tags'] as $tag) { if ($tokens[$tag]['content'] === '@see') { // Make sure the tag isn't empty. $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd); if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) { $error = 'Content missing for @see tag in function comment'; $phpcsFile->addError($error, $tag, 'EmptySees'); } } } $this->processReturn($phpcsFile, $stackPtr, $commentStart); $this->processThrows($phpcsFile, $stackPtr, $commentStart); $this->processParams($phpcsFile, $stackPtr, $commentStart); }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // Skip tokens that are the names of functions or classes // within their definitions. For example: function myFunction... // "myFunction" is T_STRING but we should skip because it is not a // function or method *call*. $functionName = $stackPtr; $findTokens = array_merge(PHP_CodeSniffer_Tokens::$emptyTokens, array(T_BITWISE_AND)); $functionKeyword = $phpcsFile->findPrevious($findTokens, $stackPtr - 1, null, true); if ($tokens[$functionKeyword]['code'] === T_FUNCTION || $tokens[$functionKeyword]['code'] === T_CLASS) { return; } // If the next non-whitespace token after the function or method call // is not an opening parenthesis then it cant really be a *call*. $openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $functionName + 1, null, true); if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { return; } $closeBracket = $tokens[$openBracket]['parenthesis_closer']; $nextSeparator = $openBracket; while (($nextSeparator = $phpcsFile->findNext(T_VARIABLE, $nextSeparator + 1, $closeBracket)) !== false) { // Make sure the variable belongs directly to this function call // and is not inside a nested function call or array. $brackets = $tokens[$nextSeparator]['nested_parenthesis']; $lastBracket = array_pop($brackets); if ($lastBracket !== $closeBracket) { continue; } // Checking this: $value = my_function(...[*]$arg...). $tokenBefore = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $nextSeparator - 1, null, true); if ($tokens[$tokenBefore]['code'] === T_BITWISE_AND) { // Checking this: $value = my_function(...[*]&$arg...). $tokenBefore = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $tokenBefore - 1, null, true); // We have to exclude all uses of T_BITWISE_AND that are not // references. We use a blacklist approach as we prefer false // positives to not identifying a pass-by-reference call at all. // The blacklist may not yet be complete. switch ($tokens[$tokenBefore]['code']) { case T_VARIABLE: case T_CLOSE_PARENTHESIS: case T_LNUMBER: // In these cases T_BITWISE_AND represents // the bitwise and operator. continue; default: // T_BITWISE_AND represents a pass-by-reference. $error = 'Call-time pass-by-reference calls are prohibited'; $phpcsFile->addError($error, $tokenBefore, 'NotAllowed'); break; } } //end if } //end while }