/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile The current file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. * @param int $currScope A pointer to the start of the scope. * * @return void */ public function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope) { $tokens = $phpcsFile->getTokens(); $function = $tokens[$stackPtr + 2]; if ($function['code'] !== T_STRING) { return; } $functionName = $function['content']; $classOpener = $tokens[$currScope]['scope_condition']; $className = $tokens[$classOpener + 2]['content']; $methodProps = $phpcsFile->getMethodProperties($stackPtr); if ($methodProps['is_static'] === true) { if (isset($tokens[$stackPtr]['scope_closer']) === false) { // There is no scope opener or closer, so the function // must be abstract. return; } $thisUsage = $stackPtr; while (($thisUsage = $phpcsFile->findNext(array(T_VARIABLE), $thisUsage + 1, $tokens[$stackPtr]['scope_closer'], false, '$this')) !== false) { if ($thisUsage === false) { return; } $error = 'Usage of "$this" in static methods will cause runtime errors'; $phpcsFile->addError($error, $thisUsage, 'Found'); } } //end if }
/** * Processes the token in the specified PHP_CodeSniffer_File. * * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this * token was found. * @param int $stackPtr The position where the token was found. * @param array $currScope The current scope opener token. * * @return void */ protected final function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope) { if ($this->currentFile !== $phpcsFile) { $this->currentFile = $phpcsFile; $this->functionOpen = false; $this->endFunction = -1; } $tokens = $phpcsFile->getTokens(); if ($stackPtr > $this->endFunction) { $this->functionOpen = false; } if ($tokens[$stackPtr]['code'] === T_FUNCTION && $this->functionOpen === false) { $this->functionOpen = true; $methodProps = $phpcsFile->getMethodProperties($stackPtr); // If the function is abstract, or is in an interface, // then set the end of the function to it's closing semicolon. if ($methodProps['is_abstract'] === true || $tokens[$currScope]['code'] === T_INTERFACE) { $this->endFunction = $phpcsFile->findNext(array(T_SEMICOLON), $stackPtr); } else { if (isset($tokens[$stackPtr]['scope_closer']) === false) { $error = 'Possible parse error: non-abstract method defined as abstract'; $phpcsFile->addWarning($error, $stackPtr, 'Internal.ParseError.NonAbstractDefinedAbstract'); return; } $this->endFunction = $tokens[$stackPtr]['scope_closer']; } } //end if if ($tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING || $tokens[$stackPtr]['code'] === T_HEREDOC) { // Check to see if this string has a variable in it. $pattern = '|(?<!\\\\)(?:\\\\{2})*\\${?[a-zA-Z0-9_]+}?|'; if (preg_match($pattern, $tokens[$stackPtr]['content']) !== 0) { $this->processVariableInString($phpcsFile, $stackPtr); } return; } if ($this->functionOpen === true) { if ($tokens[$stackPtr]['code'] === T_VARIABLE) { $this->processVariable($phpcsFile, $stackPtr); } } else { // What if we assign a member variable to another? // ie. private $_count = $this->_otherCount + 1;. $this->processMemberVar($phpcsFile, $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(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr]['code'] === T_FUNCTION) { $methodProps = $phpcsFile->getMethodProperties($stackPtr); // Abstract methods do not require a closing comment. if ($methodProps['is_abstract'] === true) { return; } // Closures do not require a closing comment. if ($methodProps['is_closure'] === true) { return; } // If this function is in an interface then we don't require // a closing comment. if ($phpcsFile->hasCondition($stackPtr, T_INTERFACE) === true) { return; } if (isset($tokens[$stackPtr]['scope_closer']) === false) { $error = 'Possible parse error: non-abstract method defined as abstract'; $phpcsFile->addWarning($error, $stackPtr, 'Abstract'); return; } $decName = $phpcsFile->getDeclarationName($stackPtr); $comment = '//end ' . $decName . '()'; } else { if ($tokens[$stackPtr]['code'] === T_CLASS) { $comment = '//end class'; } else { $comment = '//end interface'; } } //end if if (isset($tokens[$stackPtr]['scope_closer']) === false) { $error = 'Possible parse error: %s missing opening or closing brace'; $data = array($tokens[$stackPtr]['content']); $phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $data); return; } $closingBracket = $tokens[$stackPtr]['scope_closer']; if ($closingBracket === null) { // Possible inline structure. Other tests will handle it. return; } $error = 'Expected ' . $comment; if (isset($tokens[$closingBracket + 1]) === false || $tokens[$closingBracket + 1]['code'] !== T_COMMENT) { $next = $phpcsFile->findNext(T_WHITESPACE, $closingBracket + 1, null, true); if (rtrim($tokens[$next]['content']) === $comment) { // The comment isn't really missing; it is just in the wrong place. $fix = $phpcsFile->addFixableError($error . ' directly after closing brace', $closingBracket, 'Misplaced'); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); for ($i = $closingBracket + 1; $i < $next; $i++) { $phpcsFile->fixer->replaceToken($i, ''); } // Just in case, because indentation fixes can add indents onto // these comments and cause us to be unable to fix them. $phpcsFile->fixer->replaceToken($next, $comment . $phpcsFile->eolChar); $phpcsFile->fixer->endChangeset(); } } else { $fix = $phpcsFile->addFixableError($error, $closingBracket, 'Missing'); if ($fix === true) { $phpcsFile->fixer->replaceToken($closingBracket, '}' . $comment . $phpcsFile->eolChar); } } return; } //end if if (rtrim($tokens[$closingBracket + 1]['content']) !== $comment) { $fix = $phpcsFile->addFixableError($error, $closingBracket, 'Incorrect'); if ($fix === true) { $phpcsFile->fixer->replaceToken($closingBracket + 1, $comment . $phpcsFile->eolChar); } return; } }
/** * Processes the tokens within the scope. * * @param PHP_CodeSniffer_File $phpcsFile The file being processed. * @param int $stackPtr The position where this token was * found. * @param int $currScope The position of the current scope. * * @return void */ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope) { $methodName = $phpcsFile->getDeclarationName($stackPtr); if ($methodName === null) { // Ignore closures. return; } $className = $phpcsFile->getDeclarationName($currScope); $errorData = array($className . '::' . $methodName); // Is this a magic method. i.e., is prefixed with "__" ? if (preg_match('|^__|', $methodName) !== 0) { $magicPart = strtolower(substr($methodName, 2)); if (isset($this->magicMethods[$magicPart]) === false) { $error = 'Method name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore'; $phpcsFile->addError($error, $stackPtr, 'MethodDoubleUnderscore', $errorData); } return; } // PHP4 constructors are allowed to break our rules. if ($methodName === $className) { return; } // PHP4 destructors are allowed to break our rules. if ($methodName === '_' . $className) { return; } $methodProps = $phpcsFile->getMethodProperties($stackPtr); $scope = $methodProps['scope']; $scopeSpecified = $methodProps['scope_specified']; if ($methodProps['scope'] === 'private') { $isPublic = false; } else { $isPublic = true; } // If it's a private method, it must have an underscore on the front. if ($isPublic === false) { if ($methodName[0] !== '_') { $error = 'Private method name "%s" must be prefixed with an underscore'; $phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $errorData); $phpcsFile->recordMetric($stackPtr, 'Private method prefixed with underscore', 'no'); return; } else { $phpcsFile->recordMetric($stackPtr, 'Private method prefixed with underscore', 'yes'); } } // If it's not a private method, it must not have an underscore on the front. if ($isPublic === true && $scopeSpecified === true && $methodName[0] === '_') { $error = '%s method name "%s" must not be prefixed with an underscore'; $data = array(ucfirst($scope), $errorData[0]); $phpcsFile->addError($error, $stackPtr, 'PublicUnderscore', $data); return; } // If the scope was specified on the method, then the method must be // camel caps and an underscore should be checked for. If it wasn't // specified, treat it like a public method and remove the underscore // prefix if there is one because we cant determine if it is private or // public. $testMethodName = $methodName; if ($scopeSpecified === false && $methodName[0] === '_') { $testMethodName = substr($methodName, 1); } if (Common::isCamelCaps($testMethodName, false, $isPublic, false) === false) { if ($scopeSpecified === true) { $error = '%s method name "%s" is not in camel caps format'; $data = array(ucfirst($scope), $errorData[0]); $phpcsFile->addError($error, $stackPtr, 'ScopeNotCamelCaps', $data); } else { $error = 'Method name "%s" is not in camel caps format'; $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData); } return; } }
/** * Processes the tokens within the scope. * * @param PHP_CodeSniffer_File $phpcsFile The file being processed. * @param int $stackPtr The position where this token was * found. * @param int $currScope The position of the current scope. * * @return void */ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope) { $methodName = $phpcsFile->getDeclarationName($stackPtr); if ($methodName === null) { // Ignore closures. return; } $className = $phpcsFile->getDeclarationName($currScope); $errorData = array($className . '::' . $methodName); // Is this a magic method. i.e., is prefixed with "__" ? if (preg_match('|^__|', $methodName) !== 0) { $magicPart = strtolower(substr($methodName, 2)); if (isset($this->magicMethods[$magicPart]) === false && isset($this->methodsDoubleUnderscore[$magicPart]) === false) { $error = 'Method name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore'; $phpcsFile->addError($error, $stackPtr, 'MethodDoubleUnderscore', $errorData); } return; } // PHP4 constructors are allowed to break our rules. if ($methodName === $className) { return; } // PHP4 destructors are allowed to break our rules. if ($methodName === '_' . $className) { return; } // Ignore first underscore in methods prefixed with "_". $methodName = ltrim($methodName, '_'); $methodProps = $phpcsFile->getMethodProperties($stackPtr); if (Common::isCamelCaps($methodName, false, true, $this->strict) === false) { if ($methodProps['scope_specified'] === true) { $error = '%s method name "%s" is not in camel caps format'; $data = array(ucfirst($methodProps['scope']), $errorData[0]); $phpcsFile->addError($error, $stackPtr, 'ScopeNotCamelCaps', $data); } else { $error = 'Method name "%s" is not in camel caps format'; $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData); } $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'no'); return; } else { $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'yes'); } }