/** * 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 - 1]['code'] !== T_WHITESPACE) { $before = 0; } else { if ($tokens[$stackPtr - 2]['line'] !== $tokens[$stackPtr]['line']) { $before = 'newline'; } else { $before = $tokens[$stackPtr - 1]['length']; } } if ($tokens[$stackPtr + 1]['code'] !== T_WHITESPACE) { $after = 0; } else { if ($tokens[$stackPtr + 2]['line'] !== $tokens[$stackPtr]['line']) { $after = 'newline'; } else { $after = $tokens[$stackPtr + 1]['length']; } } $phpcsFile->recordMetric($stackPtr, 'Spacing before object operator', $before); $phpcsFile->recordMetric($stackPtr, 'Spacing after object operator', $after); if ($before !== 0) { $error = 'Space found before object operator'; $phpcsFile->addError($error, $stackPtr, 'Before'); } if ($after !== 0) { $error = 'Space found after object operator'; $phpcsFile->addError($error, $stackPtr, 'After'); } }
/** * 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(); $firstContent = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); // If the first non-whitespace token is not an opening parenthesis, then we are not concerned. if ($tokens[$firstContent]['code'] !== T_OPEN_PARENTHESIS) { $phpcsFile->recordMetric($stackPtr, 'Brackets around echoed strings', 'no'); return; } $end = $phpcsFile->findNext(array(T_SEMICOLON, T_CLOSE_TAG), $stackPtr, null, false); // If the token before the semi-colon is not a closing parenthesis, then we are not concerned. $prev = $phpcsFile->findPrevious(T_WHITESPACE, $end - 1, null, true); if ($tokens[$prev]['code'] !== T_CLOSE_PARENTHESIS) { $phpcsFile->recordMetric($stackPtr, 'Brackets around echoed strings', 'no'); return; } // If the parenthesis don't match, then we are not concerned. if ($tokens[$firstContent]['parenthesis_closer'] !== $prev) { $phpcsFile->recordMetric($stackPtr, 'Brackets around echoed strings', 'no'); return; } $phpcsFile->recordMetric($stackPtr, 'Brackets around echoed strings', 'yes'); if ($phpcsFile->findNext(PHP_CodeSniffer_Tokens::$operators, $stackPtr, $end, false) === false) { // There are no arithmetic operators in this. $error = 'Echoed strings should not be bracketed'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'HasBracket'); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); $phpcsFile->fixer->replaceToken($firstContent, ''); $phpcsFile->fixer->replaceToken($end - 1, ''); $phpcsFile->fixer->endChangeset(); } } }
/** * 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_DOC_COMMENT_CLOSE_TAG && $tokens[$commentEnd]['code'] !== T_COMMENT) { $phpcsFile->addError('Missing class doc comment', $stackPtr, 'Missing'); $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'no'); return; } $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'yes'); if ($tokens[$commentEnd]['code'] === T_COMMENT) { $phpcsFile->addError('You must use "/**" style comments for a class comment', $stackPtr, 'WrongStyle'); return; } if ($tokens[$commentEnd]['line'] !== $tokens[$stackPtr]['line'] - 1) { $error = 'There must be no blank lines after the class comment'; $phpcsFile->addError($error, $commentEnd, 'SpacingAfter'); } $commentStart = $tokens[$commentEnd]['comment_opener']; foreach ($tokens[$commentStart]['comment_tags'] as $tag) { $error = '%s tag is not allowed in class comment'; $data = array($tokens[$tag]['content']); $phpcsFile->addWarning($error, $tag, 'TagNotAllowed', $data); } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param integer $stackPtr The position of the current token in * the token stack. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { if ($this->_phpVersion === null) { $this->_phpVersion = PHP_CodeSniffer::getConfigData('php_version'); if ($this->_phpVersion === null) { $this->_phpVersion = PHP_VERSION_ID; } } $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]['scope_closer']) === false) { return; } $errorData = array(strtolower($tokens[$stackPtr]['content'])); $nextClass = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE, T_TRAIT), $tokens[$stackPtr]['scope_closer'] + 1); if ($nextClass !== false) { $error = 'Each %s must be in a file by itself'; $phpcsFile->addError($error, $nextClass, 'MultipleClasses', $errorData); $phpcsFile->recordMetric($stackPtr, 'One class per file', 'no'); } else { $phpcsFile->recordMetric($stackPtr, 'One class per file', 'yes'); } if ($this->_phpVersion >= 50300) { $namespace = $phpcsFile->findNext(array(T_NAMESPACE, T_CLASS, T_INTERFACE, T_TRAIT), 0); if ($tokens[$namespace]['code'] !== T_NAMESPACE) { $error = 'Each %s must be in a namespace of at least one level (a top-level vendor name)'; $phpcsFile->addError($error, $stackPtr, 'MissingNamespace', $errorData); $phpcsFile->recordMetric($stackPtr, 'Class defined in namespace', 'no'); } else { $phpcsFile->recordMetric($stackPtr, 'Class defined in namespace', 'yes'); } } }
/** * 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(); $openTag = $tokens[$stackPtr]; if ($openTag['content'] === '<?') { $error = 'Short PHP opening tag used; expected "<?php" but found "%s"'; $data = array($openTag['content']); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr, '<?php'); } $phpcsFile->recordMetric($stackPtr, 'PHP short open tag used', 'yes'); } else { $phpcsFile->recordMetric($stackPtr, 'PHP short open tag used', 'no'); } if ($openTag['code'] === T_OPEN_TAG_WITH_ECHO) { $nextVar = $tokens[$phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true)]; $error = 'Short PHP opening tag used with echo; expected "<?php echo %s ..." but found "%s %s ..."'; $data = array($nextVar['content'], $openTag['content'], $nextVar['content']); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'EchoFound', $data); if ($fix === true) { if ($tokens[$stackPtr + 1]['code'] !== T_WHITESPACE) { $phpcsFile->fixer->replaceToken($stackPtr, '<?php echo '); } else { $phpcsFile->fixer->replaceToken($stackPtr, '<?php echo'); } } } }
/** * 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(PHP_CodeSniffer_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; } $methodProps = $phpcsFile->getMethodProperties($stackPtr); if (PHP_CodeSniffer::isCamelCaps($methodName, false, true, $this->strict) === false) { if ($methodProps['scope_specified'] === true) { $error = '%s method name "%s" is not in lowerCamel format'; $data = array(ucfirst($methodProps['scope']), $errorData[0]); $phpcsFile->addError($error, $stackPtr, 'ScopeNotCamelCaps', $data); } else { $error = 'Method name "%s" is not in lowerCamel format'; $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData); } $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'no'); return; } else { $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'yes'); } }
/** * 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(); for ($i = $stackPtr + 1; $i < $phpcsFile->numTokens; $i++) { if ($tokens[$i]['column'] !== 1 || $tokens[$i]['code'] !== T_WHITESPACE && $tokens[$i]['code'] !== T_DOC_COMMENT_WHITESPACE && $tokens[$i]['code'] !== T_CONSTANT_ENCAPSED_STRING) { continue; } // If tabs are being converted to spaces, the original content // should be used instead of the converted content. if (isset($tokens[$i]['orig_content']) === true) { $content = $tokens[$i]['orig_content']; } else { $content = $tokens[$i]['content']; } if ($content[0] === ' ') { if ($tokens[$i]['code'] === T_DOC_COMMENT_WHITESPACE && $content === ' ') { // Ignore file/class-level DocBlock. continue; } // Space are considered ok if they are proceeded by tabs and not followed // by tabs, as is the case with standard docblock comments. $error = 'Tabs must be used to indent lines; spaces are not allowed'; $phpcsFile->addError($error, $i, 'SpacesUsed'); $phpcsFile->recordMetric($i, 'Line indent', 'spaces'); } else { if ($content[0] === "\t") { $phpcsFile->recordMetric($i, 'Line indent', 'tabs'); } } } //end for // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * 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(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope) { $methodName = $phpcsFile->getDeclarationName($stackPtr); if ($methodName === null) { // Ignore closures. return; } // Ignore magic methods. if (preg_match('|^__|', $methodName) !== 0) { $magicPart = strtolower(substr($methodName, 2)); if (isset($this->magicMethods[$magicPart]) === true || isset($this->methodsDoubleUnderscore[$magicPart]) === true) { return; } } $testName = ltrim($methodName, '_'); if (PHP_CodeSniffer::isCamelCaps($testName, false, true, false) === false) { $error = 'Method name "%s" is not in camel caps format'; $className = $phpcsFile->getDeclarationName($currScope); $errorData = array($className . '::' . $methodName); $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData); $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'no'); } else { $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'yes'); } }
/** * 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) { $phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'no'); 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']) { $error = 'Each PHP statement must be on a line by itself'; $phpcsFile->addError($error, $stackPtr, 'SameLine'); $phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'yes'); } else { $phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'no'); } }
/** * Processes the tokens outside the scope. * * @param PHP_CodeSniffer_File $phpcsFile The file being processed. * @param int $stackPtr The position where this token was found. */ protected function processTokenOutsideScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $functionName = $phpcsFile->getDeclarationName($stackPtr); if ($functionName === null) { // Ignore closures. return; } $errorData = [$functionName]; // Is this a magic function. i.e., it is prefixed with "__". if (preg_match('|^__|', $functionName) !== 0) { $magicPart = strtolower(substr($functionName, 2)); if (isset($this->magicFunctions[$magicPart]) === false) { $error = 'Function name "%s" is invalid; only PHP magic methods' . ' should be prefixed with a double underscore'; $phpcsFile->addError($error, $stackPtr, 'FunctionDoubleUnderscore', $errorData); } return; } // Ignore first underscore in functions prefixed with "_". $functionName = ltrim($functionName, '_'); if (preg_match('/^[a-z][_a-z]*$/', $functionName) === false) { $error = 'Function name "%s" is not in snake case format'; $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData); $phpcsFile->recordMetric($stackPtr, 'snake_case function name', 'no'); } else { $phpcsFile->recordMetric($stackPtr, 'snake_case method name', 'yes'); } }
/** * 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(); // Make sure this file only contains PHP code. for ($i = 0; $i < $phpcsFile->numTokens; $i++) { if ($tokens[$i]['code'] === T_INLINE_HTML && trim($tokens[$i]['content']) !== '') { return $phpcsFile->numTokens; } } // Find the last non-empty token. for ($last = $phpcsFile->numTokens - 1; $last > 0; $last--) { if (trim($tokens[$last]['content']) !== '') { break; } } if ($tokens[$last]['code'] === T_CLOSE_TAG) { $error = 'A closing tag is not permitted at the end of a PHP file'; $fix = $phpcsFile->addFixableError($error, $last, 'NotAllowed'); if ($fix === true) { $phpcsFile->fixer->replaceToken($last, ''); } $phpcsFile->recordMetric($stackPtr, 'PHP closing tag at end of PHP-only file', 'yes'); } else { $phpcsFile->recordMetric($stackPtr, 'PHP closing tag at end of PHP-only file', 'no'); } // Ignore the rest of the file. return $phpcsFile->numTokens; }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile The current file being processed. * @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 (isset($tokens[$stackPtr]['scope_opener']) === false) { $error = 'Possible parse error: %s missing opening or closing brace'; $data = array($tokens[$stackPtr]['content']); $phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $data); return; } // Determine the name of the class or interface. Note that we cannot // simply look for the first T_STRING because a class name // starting with the number will be multiple tokens. $opener = $tokens[$stackPtr]['scope_opener']; $nameStart = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, $opener, true); $nameEnd = $phpcsFile->findNext(T_WHITESPACE, $nameStart, $opener); $name = trim($phpcsFile->getTokensAsString($nameStart, $nameEnd - $nameStart)); // Check for camel caps format. $valid = PHP_CodeSniffer::isCamelCaps($name, true, true, false); if ($valid === false) { $type = ucfirst($tokens[$stackPtr]['content']); $error = '%s name "%s" is not in camel caps format'; $data = array($type, $name); $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $data); $phpcsFile->recordMetric($stackPtr, 'CamelCase class name', 'no'); } else { $phpcsFile->recordMetric($stackPtr, 'CamelCase class name', 'yes'); } }
/** * 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) { $this->currentFile = $phpcsFile; $tokens = $phpcsFile->getTokens(); $type = strtolower($tokens[$stackPtr]['content']); $errorData = array($type); $find = PHP_CodeSniffer_Tokens::$methodPrefixes; $find[] = T_WHITESPACE; $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1, null, true); if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG && $tokens[$commentEnd]['code'] !== T_COMMENT) { $phpcsFile->addError('Missing class doc comment', $stackPtr, 'Missing'); $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'no'); return; } else { $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'yes'); } // Try and determine if this is a file comment instead of a class comment. // We assume that if this is the first comment after the open PHP tag, then // it is most likely a file comment instead of a class comment. if ($tokens[$commentEnd]['code'] === T_DOC_COMMENT_CLOSE_TAG) { $start = $tokens[$commentEnd]['comment_opener'] - 1; } else { $start = $phpcsFile->findPrevious(T_COMMENT, $commentEnd - 1, null, true); } if ($tokens[$commentEnd]['code'] === T_COMMENT) { $phpcsFile->addError('You must use "/**" style comments for a class comment', $stackPtr, 'WrongStyle'); return; } // Check each tag. $this->processTags($phpcsFile, $stackPtr, $tokens[$commentEnd]['comment_opener']); }
/** * 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(); for ($i = $stackPtr + 1; $i < $phpcsFile->numTokens; $i++) { if ($tokens[$i]['column'] !== 1 || $tokens[$i]['code'] !== T_WHITESPACE) { continue; } if ($tokens[$i]['content'][0] === "\t") { $error = 'Spaces must be used to indent lines; tabs are not allowed'; $fix = $phpcsFile->addFixableError($error, $i, 'TabsUsed'); $phpcsFile->recordMetric($i, 'Line indent', 'tabs'); if ($fix === true && $phpcsFile->fixer->enabled === true) { // Replace tabs with spaces, usign an indent of 4 spaces. // Other sniffs can then correct the indent if they need to. $newContent = str_replace("\t", ' ', $tokens[$i]['content']); $phpcsFile->fixer->replaceToken($i, $newContent); } } else { if ($tokens[$i]['content'][0] === ' ') { $phpcsFile->recordMetric($i, 'Line indent', 'spaces'); } } } // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param integer $stackPtr The position of the current token in * the token stack. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]['scope_closer']) === false) { return; } $className = $phpcsFile->getDeclarationName($stackPtr); $errorData = array(strtolower($tokens[$stackPtr]['content'])); $nextClass = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE, T_TRAIT), $tokens[$stackPtr]['scope_closer'] + 1); if ($nextClass !== false) { $nextClassName = $phpcsFile->getDeclarationName($nextClass); $extends = $phpcsFile->findExtendedClassName($nextClass); if ($className == $nextClassName && $extends != $className && substr($extends, -1 * (strlen($className) + 1)) == '\\' . $className) { // $nextClassName wraps $className in global namespace (probably) $phpcsFile->recordMetric($stackPtr, 'One class per file', 'yes'); } else { $error = 'Each %s must be in a file by itself'; $phpcsFile->addError($error, $nextClass, 'MultipleClasses', $errorData); $phpcsFile->recordMetric($stackPtr, 'One class per file', 'no'); } } else { $phpcsFile->recordMetric($stackPtr, 'One class per file', 'yes'); } if (version_compare(PHP_VERSION, '5.3.0') >= 0) { $namespace = $phpcsFile->findNext(array(T_NAMESPACE, T_CLASS, T_INTERFACE, T_TRAIT), 0); if ($tokens[$namespace]['code'] !== T_NAMESPACE) { $error = 'Each %s must be in a namespace of at least one level (a top-level vendor name)'; $phpcsFile->addWarning($error, $stackPtr, 'MissingNamespace', $errorData); $phpcsFile->recordMetric($stackPtr, 'Class defined in namespace', 'no'); } else { $phpcsFile->recordMetric($stackPtr, 'Class defined in namespace', 'yes'); } } }
/** * 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(); $type = strtolower($tokens[$stackPtr]['content']); $errorData = array($type); $find = PHP_CodeSniffer_Tokens::$methodPrefixes; $find[] = T_WHITESPACE; $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1, null, true); if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG && $tokens[$commentEnd]['code'] !== T_COMMENT) { $phpcsFile->addError('Missing %s doc comment', $stackPtr, 'Missing', $errorData); $phpcsFile->recordMetric($stackPtr, '%s has doc comment', 'no'); return; } else { $phpcsFile->recordMetric($stackPtr, '%s has doc comment', 'yes'); } // Try and determine if this is a file comment instead of a class comment. // We assume that if this is the first comment after the open PHP tag, then // it is most likely a file comment instead of a class comment. if ($tokens[$commentEnd]['code'] === T_DOC_COMMENT_CLOSE_TAG) { $start = $tokens[$commentEnd]['comment_opener'] - 1; } else { $start = $phpcsFile->findPrevious(T_COMMENT, $commentEnd - 1, null, true); } $prev = $phpcsFile->findPrevious(T_WHITESPACE, $start, null, true); if ($tokens[$prev]['code'] === T_OPEN_TAG) { $prevOpen = $phpcsFile->findPrevious(T_OPEN_TAG, $prev - 1); if ($prevOpen === false) { // This is a comment directly after the first open tag, // so probably a file comment. $phpcsFile->addError('Missing %s doc comment', $stackPtr, 'Missing', $errorData); return; } } if ($tokens[$commentEnd]['code'] === T_COMMENT) { $phpcsFile->addError('You must use "/**" style comments for a %s comment', $stackPtr, 'WrongStyle', $errorData); return; } if ($tokens[$commentEnd]['line'] !== $tokens[$stackPtr]['line'] - 1) { $error = 'There must be no blank lines after the %s comment'; $phpcsFile->addError($error, $commentEnd, 'SpacingAfter', $errorData); } $commentStart = $tokens[$commentEnd]['comment_opener']; if ($tokens[$prev]['line'] !== $tokens[$commentStart]['line'] - 2) { $error = 'There must be exactly one blank line before the %s comment'; $phpcsFile->addError($error, $commentStart, 'SpacingBefore', $errorData); } // Class doc comments should provide an @author tag. $hasAuthorTag = false; foreach ($tokens[$commentStart]['comment_tags'] as $tag) { if ($tokens[$tag]['content'] === '@author') { $hasAuthorTag = true; break; } } if ($hasAuthorTag === false) { $phpcsFile->addWarning('The doc comment on class level should provide an @author tag.', $commentStart, 'NoAuthorTag'); } }
/** * 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) { $this->spacing = (int) $this->spacing; $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr - 1]['code'] !== T_WHITESPACE) { $before = 0; } else { if ($tokens[$stackPtr - 2]['line'] !== $tokens[$stackPtr]['line']) { $before = 'newline'; } else { $before = $tokens[$stackPtr - 1]['length']; } } if ($tokens[$stackPtr + 1]['code'] !== T_WHITESPACE) { $after = 0; } else { if ($tokens[$stackPtr + 2]['line'] !== $tokens[$stackPtr]['line']) { $after = 'newline'; } else { $after = $tokens[$stackPtr + 1]['length']; } } $phpcsFile->recordMetric($stackPtr, 'Spacing before string concat', $before); $phpcsFile->recordMetric($stackPtr, 'Spacing after string concat', $after); if ($before === $this->spacing && $after === $this->spacing) { return; } if ($this->spacing === 0) { $message = 'Concat operator must not be surrounded by spaces'; $data = array(); } else { if ($this->spacing > 1) { $message = 'Concat operator must be surrounded by %s spaces'; } else { $message = 'Concat operator must be surrounded by a single space'; } $data = array($this->spacing); } $fix = $phpcsFile->addFixableError($message, $stackPtr, 'PaddingFound', $data); if ($fix === true) { $padding = str_repeat(' ', $this->spacing); if ($tokens[$stackPtr - 1]['code'] === T_WHITESPACE) { $phpcsFile->fixer->replaceToken($stackPtr - 1, $padding); } else { if ($this->spacing > 0) { $phpcsFile->fixer->addContent($stackPtr - 1, $padding); } } if ($tokens[$stackPtr + 1]['code'] === T_WHITESPACE) { $phpcsFile->fixer->replaceToken($stackPtr + 1, $padding); } else { if ($this->spacing > 0) { $phpcsFile->fixer->addContent($stackPtr, $padding); } } } }
/** * 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 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 integer $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(); $errorData = array(strtolower($tokens[$stackPtr]['content'])); if (isset($tokens[$stackPtr]['scope_opener']) === false) { $error = 'Possible parse error: %s missing opening or closing brace'; $phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $errorData); return; } $curlyBrace = $tokens[$stackPtr]['scope_opener']; $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, $curlyBrace - 1, $stackPtr, true); $classLine = $tokens[$lastContent]['line']; $braceLine = $tokens[$curlyBrace]['line']; if ($braceLine === $classLine) { $phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'same line'); } else { $phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'new line'); $error = 'Opening brace of a %s must be on the same line following the %s declaration'; $data = array($tokens[$stackPtr]['content'], $tokens[$stackPtr]['content']); $fix = $phpcsFile->addFixableError($error, $curlyBrace, 'OpenBraceWrongLine', $data); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); for ($i = $curlyBrace - 1; $i > $lastContent; $i--) { $phpcsFile->fixer->replaceToken($i, ' '); } $phpcsFile->fixer->endChangeset(); } return; } // end if if ($tokens[$curlyBrace - 1]['code'] === T_WHITESPACE) { $prevContent = $tokens[$curlyBrace - 1]['content']; $blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar)); $spaces = strlen($blankSpace); $expected = 1; if ($spaces !== $expected) { $error = 'Expected %s spaces before opening brace; %s found'; $data = array($expected, $spaces); $fix = $phpcsFile->addFixableError($error, $curlyBrace, 'SpaceBeforeBrace', $data); if ($fix === true) { $indent = str_repeat(' ', $expected); if ($spaces === 0) { $phpcsFile->fixer->addContentBefore($curlyBrace, $indent); } else { $phpcsFile->fixer->replaceToken($curlyBrace - 1, $indent); } } } } else { $error = 'Expected 1 space before opening brace; none found'; $fix = $phpcsFile->addFixableError($error, $curlyBrace, 'SpaceBeforeBrace'); if ($fix === true) { $phpcsFile->fixer->addContentBefore($curlyBrace, ' '); } } }
/** * 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 int */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // Find the next non whitespace token. $commentStart = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); // Allow declare() statements at the top of the file. if ($tokens[$commentStart]['code'] === T_DECLARE) { $semicolon = $phpcsFile->findNext(T_SEMICOLON, $commentStart + 1); $commentStart = $phpcsFile->findNext(T_WHITESPACE, $semicolon + 1, null, true); } // Ignore vim header. if ($tokens[$commentStart]['code'] === T_COMMENT) { if (strstr($tokens[$commentStart]['content'], 'vim:') !== false) { $commentStart = $phpcsFile->findNext(T_WHITESPACE, $commentStart + 1, null, true); } } $errorToken = $stackPtr + 1; if (isset($tokens[$errorToken]) === false) { $errorToken--; } if ($tokens[$commentStart]['code'] === T_CLOSE_TAG) { // We are only interested if this is the first open tag. return $phpcsFile->numTokens + 1; } elseif ($tokens[$commentStart]['code'] === T_COMMENT) { $error = 'You must use "/**" style comments for a file comment'; $phpcsFile->addError($error, $errorToken, 'WrongStyle'); $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes'); return $phpcsFile->numTokens + 1; } elseif ($commentStart === false || $tokens[$commentStart]['code'] !== T_DOC_COMMENT_OPEN_TAG) { $phpcsFile->addError('Missing file doc comment', $errorToken, 'Missing'); $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'no'); return $phpcsFile->numTokens + 1; } else { $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes'); } // Check the PHP Version, which should be in some text before the first tag. $commentEnd = $tokens[$commentStart]['comment_closer']; $found = false; for ($i = $commentStart + 1; $i < $commentEnd; $i++) { if ($tokens[$i]['code'] === T_DOC_COMMENT_TAG) { break; } elseif ($tokens[$i]['code'] === T_DOC_COMMENT_STRING && strstr(strtolower($tokens[$i]['content']), 'php version') !== false) { $found = true; break; } } if ($found === false) { $error = 'PHP version not specified'; $phpcsFile->addWarning($error, $commentEnd, 'MissingVersion'); } // Check each tag. $this->processTags($phpcsFile, $stackPtr, $commentStart); // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // Find the next non whitespace token. $commentStart = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); // Allow declare() statements at the top of the file. if ($tokens[$commentStart]['code'] === T_DECLARE) { $semicolon = $phpcsFile->findNext(T_SEMICOLON, $commentStart + 1); $commentStart = $phpcsFile->findNext(T_WHITESPACE, $semicolon + 1, null, true); } // Ignore vim header. if ($tokens[$commentStart]['code'] === T_COMMENT) { if (strstr($tokens[$commentStart]['content'], 'vim:') !== false) { $commentStart = $phpcsFile->findNext(T_WHITESPACE, $commentStart + 1, null, true); } } $errorToken = $stackPtr + 1; if (isset($tokens[$errorToken]) === false) { $errorToken--; } if ($tokens[$commentStart]['code'] === T_CLOSE_TAG) { // We are only interested if this is the first open tag. return $phpcsFile->numTokens + 1; } else { if ($tokens[$commentStart]['code'] === T_COMMENT) { $error = 'You must use "/**" style comments for a file comment'; $phpcsFile->addError($error, $errorToken, 'WrongStyle'); $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes'); return $phpcsFile->numTokens + 1; } else { if ($commentStart === false || $tokens[$commentStart]['code'] !== T_DOC_COMMENT_OPEN_TAG) { $phpcsFile->addError('Missing file doc comment', $errorToken, 'Missing'); $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'no'); return $phpcsFile->numTokens + 1; } } } $commentEnd = $tokens[$commentStart]['comment_closer']; $nextToken = $phpcsFile->findNext(T_WHITESPACE, $commentEnd + 1, null, true); $ignore = array(T_CLASS, T_INTERFACE, T_TRAIT, T_FUNCTION, T_CLOSURE, T_PUBLIC, T_PRIVATE, T_PROTECTED, T_FINAL, T_STATIC, T_ABSTRACT, T_CONST, T_PROPERTY, T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE); if (in_array($tokens[$nextToken]['code'], $ignore) === true) { $phpcsFile->addError('Missing file doc comment', $stackPtr, 'Missing'); $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'no'); return $phpcsFile->numTokens + 1; } $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes'); // Check each tag. $this->processTags($phpcsFile, $stackPtr, $commentStart); // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * 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) { if ($phpcsFile->findNext(T_INLINE_HTML, $stackPtr + 1) !== false) { return $phpcsFile->numTokens + 1; } // Skip to the end of the file. $tokens = $phpcsFile->getTokens(); $lastToken = $phpcsFile->numTokens - 1; if ($tokens[$lastToken]['content'] === '') { $lastToken--; } // Hard-coding the expected \n in this sniff as it is PSR-2 specific and // PSR-2 enforces the use of unix style newlines. if (substr($tokens[$lastToken]['content'], -1) !== "\n") { $error = 'Expected 1 newline at end of file; 0 found'; $fix = $phpcsFile->addFixableError($error, $lastToken, 'NoneFound'); if ($fix === true) { $phpcsFile->fixer->addNewline($lastToken); } $phpcsFile->recordMetric($stackPtr, 'Number of newlines at EOF', '0'); return $phpcsFile->numTokens + 1; } // Go looking for the last non-empty line. $lastLine = $tokens[$lastToken]['line']; if ($tokens[$lastToken]['code'] === T_WHITESPACE || $tokens[$lastToken]['code'] === T_DOC_COMMENT_WHITESPACE) { $lastCode = $phpcsFile->findPrevious(array(T_WHITESPACE, T_DOC_COMMENT_WHITESPACE), $lastToken - 1, null, true); } else { $lastCode = $lastToken; } $lastCodeLine = $tokens[$lastCode]['line']; $blankLines = $lastLine - $lastCodeLine + 1; $phpcsFile->recordMetric($stackPtr, 'Number of newlines at EOF', $blankLines); if ($blankLines > 1) { $error = 'Expected 1 blank line at end of file; %s found'; $data = array($blankLines); $fix = $phpcsFile->addFixableError($error, $lastCode, 'TooMany', $data); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); $phpcsFile->fixer->replaceToken($lastCode, rtrim($tokens[$lastCode]['content'])); for ($i = $lastCode + 1; $i < $lastToken; $i++) { $phpcsFile->fixer->replaceToken($i, ''); } $phpcsFile->fixer->replaceToken($lastToken, $phpcsFile->eolChar); $phpcsFile->fixer->endChangeset(); } } // Skip the rest of the file. return $phpcsFile->numTokens + 1; }
/** * 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 int */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $fileName = basename($phpcsFile->getFilename()); $lowercaseFileName = strtolower($fileName); if ($fileName !== $lowercaseFileName) { $data = array($fileName, $lowercaseFileName); $error = 'Filename "%s" doesn\'t match the expected filename "%s"'; $phpcsFile->addError($error, $stackPtr, 'NotFound', $data); $phpcsFile->recordMetric($stackPtr, 'Lowercase filename', 'no'); } else { $phpcsFile->recordMetric($stackPtr, 'Lowercase filename', 'yes'); } // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * 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 int */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $found = $phpcsFile->eolChar; $found = str_replace("\n", '\\n', $found); $found = str_replace("\r", '\\r', $found); $phpcsFile->recordMetric($stackPtr, 'EOL char', $found); if ($found === $this->eolChar) { // Ignore the rest of the file. return $phpcsFile->numTokens + 1; } // Check for single line files without an EOL. This is a very special // case and the EOL char is set to \n when this happens. if ($found === '\\n') { $tokens = $phpcsFile->getTokens(); $lastToken = $phpcsFile->numTokens - 1; if ($tokens[$lastToken]['line'] === 1 && $tokens[$lastToken]['content'] !== "\n") { return; } } $error = 'End of line character is invalid; expected "%s" but found "%s"'; $expected = $this->eolChar; $expected = str_replace("\n", '\\n', $expected); $expected = str_replace("\r", '\\r', $expected); $data = array($expected, $found); // Errors are always reported on line 1, no matter where the first PHP tag is. $fix = $phpcsFile->addFixableError($error, 0, 'InvalidEOLChar', $data); if ($fix === true) { $tokens = $phpcsFile->getTokens(); switch ($this->eolChar) { case '\\n': $eolChar = "\n"; break; case '\\r': $eolChar = "\r"; break; case '\\r\\n': $eolChar = "\r\n"; break; default: $eolChar = $this->eolChar; break; } for ($i = 0; $i < $phpcsFile->numTokens; $i++) { if (isset($tokens[$i + 1]) === false || $tokens[$i + 1]['line'] > $tokens[$i]['line']) { // Token is the last on a line. if (isset($tokens[$i]['orig_content']) === true) { $tokenContent = $tokens[$i]['orig_content']; } else { $tokenContent = $tokens[$i]['content']; } $newContent = rtrim($tokenContent, "\r\n"); $newContent .= $eolChar; $phpcsFile->fixer->replaceToken($i, $newContent); } } } //end if // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * 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 int */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $found = $phpcsFile->eolChar; $found = str_replace("\n", '\\n', $found); $found = str_replace("\r", '\\r', $found); $phpcsFile->recordMetric($stackPtr, 'EOL char', $found); if ($found !== $this->eolChar) { // Check for single line files without an EOL. This is a very special // case and the EOL char is set to \n when this happens. if ($found === '\\n') { $tokens = $phpcsFile->getTokens(); $lastToken = $phpcsFile->numTokens - 1; if ($tokens[$lastToken]['line'] === 1 && $tokens[$lastToken]['content'] !== "\n") { return; } } $error = 'End of line character is invalid; expected "%s" but found "%s"'; $expected = $this->eolChar; $expected = str_replace("\n", '\\n', $expected); $expected = str_replace("\r", '\\r', $expected); $data = array($expected, $found); $phpcsFile->addError($error, $stackPtr, 'InvalidEOLChar', $data); } //end if // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * 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) { $this->currentFile = $phpcsFile; $tokens = $phpcsFile->getTokens(); $type = strtolower($tokens[$stackPtr]['content']); $errorData = array($type); $find = PHP_CodeSniffer_Tokens::$methodPrefixes; $find[] = T_WHITESPACE; $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1, null, true); if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG && $tokens[$commentEnd]['code'] !== T_COMMENT) { $fix = $phpcsFile->addFixableError('Missing class doc comment', $stackPtr, 'Missing'); if (true === $fix) { $className = $tokens[$stackPtr + 2]['content']; $packageName = $this->findNamespace($tokens); $comment = sprintf("/**" . $phpcsFile->eolChar . " * Class %s" . $phpcsFile->eolChar . ($packageName ? sprintf(" * @package %s" . $phpcsFile->eolChar, $packageName) : '') . " */" . $phpcsFile->eolChar, $className); $phpcsFile->fixer->addContentBefore($stackPtr, $comment); } $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'no'); return; } else { $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'yes'); } // Try and determine if this is a file comment instead of a class comment. // We assume that if this is the first comment after the open PHP tag, then // it is most likely a file comment instead of a class comment. if ($tokens[$commentEnd]['code'] === T_DOC_COMMENT_CLOSE_TAG) { $start = $tokens[$commentEnd]['comment_opener'] - 1; } else { $start = $phpcsFile->findPrevious(T_COMMENT, $commentEnd - 1, null, true); } $prev = $phpcsFile->findPrevious(T_WHITESPACE, $start, null, true); if ($tokens[$prev]['code'] === T_OPEN_TAG) { $prevOpen = $phpcsFile->findPrevious(T_OPEN_TAG, $prev - 1); if ($prevOpen === false) { // This is a comment directly after the first open tag, // so probably a file comment. $phpcsFile->addError('Missing class doc comment', $stackPtr, 'Missing'); return; } } if ($tokens[$commentEnd]['code'] === T_COMMENT) { $phpcsFile->addError('You must use "/**" style comments for a class comment', $stackPtr, 'WrongStyle'); return; } // Check each tag. $this->processTags($phpcsFile, $stackPtr, $tokens[$commentEnd]['comment_opener']); }
/** * 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(); $last = $phpcsFile->findPrevious(array(T_INLINE_HTML, T_WHITESPACE), $phpcsFile->numTokens - 1, null, true); if ($tokens[$last]['code'] === T_CLOSE_TAG) { $error = 'A closing tag is not permitted at the end of a PHP file'; $fix = $phpcsFile->addFixableError($error, $last, 'NotAllowed'); if ($fix === true && $phpcsFile->fixer->enabled === true) { $phpcsFile->fixer->replaceToken($last, ''); } $phpcsFile->recordMetric($stackPtr, 'PHP closing tag at EOF', 'yes'); } else { $phpcsFile->recordMetric($stackPtr, 'PHP closing tag at EOF', 'no'); } // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * 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) { $this->requiredSpacesAfterOpen = (int) $this->requiredSpacesAfterOpen; $this->requiredSpacesBeforeClose = (int) $this->requiredSpacesBeforeClose; $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]['parenthesis_opener']) === false || isset($tokens[$stackPtr]['parenthesis_closer']) === false) { return; } $parenOpener = $tokens[$stackPtr]['parenthesis_opener']; $parenCloser = $tokens[$stackPtr]['parenthesis_closer']; $spaceAfterOpen = 0; if ($tokens[$parenOpener + 1]['code'] === T_WHITESPACE) { if (strpos($tokens[$parenOpener + 1]['content'], $phpcsFile->eolChar) !== false) { $spaceAfterOpen = 'newline'; } else { $spaceAfterOpen = strlen($tokens[$parenOpener + 1]['content']); } } $phpcsFile->recordMetric($stackPtr, 'Spaces after control structure open parenthesis', $spaceAfterOpen); if ($spaceAfterOpen !== $this->requiredSpacesAfterOpen) { $error = 'Expected %s spaces after opening bracket; %s found'; $data = array($this->requiredSpacesAfterOpen, $spaceAfterOpen); $fix = $phpcsFile->addFixableError($error, $parenOpener + 1, 'SpacingAfterOpenBrace', $data); if ($fix === true) { $padding = str_repeat(' ', $this->requiredSpacesAfterOpen); if ($spaceAfterOpen === 0) { $phpcsFile->fixer->addContent($parenOpener, $padding); } else { if ($spaceAfterOpen === 'newline') { $phpcsFile->fixer->replaceToken($parenOpener + 1, ''); } else { $phpcsFile->fixer->replaceToken($parenOpener + 1, $padding); } } } } if ($tokens[$parenOpener]['line'] === $tokens[$parenCloser]['line']) { $spaceBeforeClose = 0; if ($tokens[$parenCloser - 1]['code'] === T_WHITESPACE) { $spaceBeforeClose = strlen(ltrim($tokens[$parenCloser - 1]['content'], $phpcsFile->eolChar)); } $phpcsFile->recordMetric($stackPtr, 'Spaces before control structure close parenthesis', $spaceBeforeClose); if ($spaceBeforeClose !== $this->requiredSpacesBeforeClose) { $error = 'Expected %s spaces before closing bracket; %s found'; $data = array($this->requiredSpacesBeforeClose, $spaceBeforeClose); $fix = $phpcsFile->addFixableError($error, $parenCloser - 1, 'SpaceBeforeCloseBrace', $data); if ($fix === true) { $padding = str_repeat(' ', $this->requiredSpacesBeforeClose); if ($spaceBeforeClose === 0) { $phpcsFile->fixer->addContentBefore($parenCloser, $padding); } else { $phpcsFile->fixer->replaceToken($parenCloser - 1, $padding); } } } } //end if }
/** * 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 int */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $fileName = basename($phpcsFile->getFilename()); $tokens = $phpcsFile->getTokens(); $nextClass = $phpcsFile->findNext([T_CLASS, T_INTERFACE, T_TRAIT], $stackPtr + 1); // If there are no classes, interfaces, or traits, ignore. if ($nextClass == null) { return $phpcsFile->numTokens + 1; } $expected = $tokens[$nextClass + 2]['content'] . '.php'; if ($expected !== $fileName) { $phpcsFile->addError('Filename "%s" doesn\'t match the expected filename "%s"', $stackPtr, 'NotFound', [$fileName, $expected]); $phpcsFile->recordMetric($stackPtr, 'PSR-0 filename', 'no'); } $phpcsFile->recordMetric($stackPtr, 'PSR-0 filename', 'yes'); // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }