/** * 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(); $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(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 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(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->beginChangeset(); $phpcsFile->fixer->replaceToken($last, $phpcsFile->eolChar); $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, $last - 1, null, true); if ($tokens[$prev]['code'] !== T_SEMICOLON && $tokens[$prev]['code'] !== T_CLOSE_CURLY_BRACKET) { $phpcsFile->fixer->addContent($prev, ';'); } $phpcsFile->fixer->endChangeset(); } $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 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; } // 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 ($testName !== '' && Common::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(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $find = 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 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(); $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(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 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(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); if ($nameEnd === false) { $name = $tokens[$nameStart]['content']; } else { $name = trim($phpcsFile->getTokensAsString($nameStart, $nameEnd - $nameStart)); } // Check for camel caps format. $valid = Common::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 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(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 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(File $phpcsFile, $stackPtr) { if ($this->tabWidth === null) { if (isset($phpcsFile->config->tabWidth) === false || $phpcsFile->config->tabWidth === 0) { // We have no idea how wide tabs are, so assume 4 spaces for fixing. // It shouldn't really matter because indent checks elsewhere in the // standard should fix things up. $this->tabWidth = 4; } else { $this->tabWidth = $phpcsFile->config->tabWidth; } } $checkTokens = array(T_WHITESPACE => true, T_INLINE_HTML => true, T_DOC_COMMENT_WHITESPACE => true); $tokens = $phpcsFile->getTokens(); for ($i = $stackPtr + 1; $i < $phpcsFile->numTokens; $i++) { if ($tokens[$i]['column'] !== 1 || isset($checkTokens[$tokens[$i]['code']]) === false) { 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. $phpcsFile->recordMetric($i, 'Line indent', 'spaces'); $error = 'Tabs must be used to indent lines; spaces are not allowed'; $fix = $phpcsFile->addFixableError($error, $i, 'SpacesUsed'); if ($fix === true) { $trimmed = ltrim($content, ' '); $numSpaces = strlen($content) - strlen($trimmed); if ($numSpaces < $this->tabWidth) { $numTabs = 1; $padding = "\t"; } else { $numTabs = floor($numSpaces / $this->tabWidth); $remaining = $numSpaces - $numTabs * $this->tabWidth; $padding = str_repeat("\t", $numTabs) . ($padding = str_repeat(' ', $remaining)); } $phpcsFile->fixer->replaceToken($i, $padding . $trimmed); } } else { if ($content[0] === "\t") { $phpcsFile->recordMetric($i, 'Line indent', 'tabs'); } } //end if } //end for // 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(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; // 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) { $lastCode = $phpcsFile->findPrevious(T_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 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(); $find = 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'); } } } $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(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(File $phpcsFile, $stackPtr) { $filename = $phpcsFile->getFilename(); if ($filename === 'STDIN') { return; } $filename = basename($filename); $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 void */ public function process(File $phpcsFile, $stackPtr) { // The BOM will be the very first token in the file. if ($stackPtr !== 0) { return; } $tokens = $phpcsFile->getTokens(); foreach ($this->bomDefinitions as $bomName => $expectedBomHex) { $bomByteLength = strlen($expectedBomHex) / 2; $htmlBomHex = bin2hex(substr($tokens[$stackPtr]['content'], 0, $bomByteLength)); if ($htmlBomHex === $expectedBomHex) { $errorData = array($bomName); $error = 'File contains %s byte order mark, which may corrupt your application'; $phpcsFile->addError($error, $stackPtr, 'Found', $errorData); $phpcsFile->recordMetric($stackPtr, 'Using byte order mark', 'yes'); return; } } $phpcsFile->recordMetric($stackPtr, 'Using byte order mark', 'no'); }
/** * 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(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $keyword = $tokens[$stackPtr]['content']; if (strtolower($keyword) !== $keyword) { if ($keyword === strtoupper($keyword)) { $phpcsFile->recordMetric($stackPtr, 'PHP keyword case', 'upper'); } else { $phpcsFile->recordMetric($stackPtr, 'PHP keyword case', 'mixed'); } $error = 'PHP keywords must be lowercase; expected "%s" but found "%s"'; $data = array(strtolower($keyword), $keyword); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr, strtolower($keyword)); } } else { $phpcsFile->recordMetric($stackPtr, 'PHP keyword case', 'lower'); } }
/** * 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 + 1]['code'] !== T_WHITESPACE) { $error = 'A cast statement must be followed by a single space'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpace'); if ($fix === true) { $phpcsFile->fixer->addContent($stackPtr, ' '); } $phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', 0); return; } $phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', $tokens[$stackPtr + 1]['length']); if ($tokens[$stackPtr + 1]['length'] !== 1) { $error = 'A cast statement must be followed by a single space'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'TooMuchSpace'); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr + 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(File $phpcsFile, $stackPtr) { $this->currentFile = $phpcsFile; $tokens = $phpcsFile->getTokens(); $type = strtolower($tokens[$stackPtr]['content']); $errorData = array($type); $find = 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; } // 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(File $phpcsFile, $stackPtr) { // Find the last non-empty token. $tokens = $phpcsFile->getTokens(); 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 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(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr]['code'] === T_ELSEIF) { $phpcsFile->recordMetric($stackPtr, 'Use of ELSE IF or ELSEIF', 'elseif'); return; } $next = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); if ($tokens[$next]['code'] === T_IF) { $phpcsFile->recordMetric($stackPtr, 'Use of ELSE IF or ELSEIF', 'else if'); $error = 'Usage of ELSE IF is discouraged; use ELSEIF instead'; $fix = $phpcsFile->addFixableWarning($error, $stackPtr, 'NotAllowed'); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); $phpcsFile->fixer->replaceToken($stackPtr, 'elseif'); for ($i = $stackPtr + 1; $i <= $next; $i++) { $phpcsFile->fixer->replaceToken($i, ''); } $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(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 && ($before !== 'newline' || $this->ignoreNewlines === false)) { $error = 'Space found before object operator'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Before'); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr - 1, ''); } } if ($after !== 0 && ($after !== 'newline' || $this->ignoreNewlines === false)) { $error = 'Space found after object operator'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'After'); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr + 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(File $phpcsFile, $stackPtr) { // Skip to the end of the file. $tokens = $phpcsFile->getTokens(); $stackPtr = $phpcsFile->numTokens - 1; if ($phpcsFile->tokenizerType !== 'PHP') { $stackPtr--; } $eolCharLen = strlen($phpcsFile->eolChar); $lastChars = substr($tokens[$stackPtr]['content'], $eolCharLen * -1); if ($lastChars !== $phpcsFile->eolChar) { $phpcsFile->recordMetric($stackPtr, 'Newline at EOF', 'no'); $error = 'File must end with a newline character'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotFound'); if ($fix === true) { $phpcsFile->fixer->addNewline($stackPtr); } } else { $phpcsFile->recordMetric($stackPtr, 'Newline at EOF', 'yes'); } // 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(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr]['content'][0] === '#') { $phpcsFile->recordMetric($stackPtr, 'Inline comment style', '# ...'); $error = 'Perl-style comments are not allowed. Use "// Comment."'; $error .= ' or "/* comment */" instead.'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'WrongStyle'); if ($fix === true) { $newComment = ltrim($tokens[$stackPtr]['content'], '# '); $newComment = '// ' . $newComment; $phpcsFile->fixer->replaceToken($stackPtr, $newComment); } } else { if ($tokens[$stackPtr]['content'][0] === '/' && $tokens[$stackPtr]['content'][1] === '/') { $phpcsFile->recordMetric($stackPtr, 'Inline comment style', '// ...'); } else { if ($tokens[$stackPtr]['content'][0] === '/' && $tokens[$stackPtr]['content'][1] === '*') { $phpcsFile->recordMetric($stackPtr, 'Inline comment style', '/* ... */'); } } } }
/** * 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(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $fileName = $phpcsFile->getFileName(); $extension = substr($fileName, strrpos($fileName, '.')); $nextClass = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE, T_TRAIT), $stackPtr); if ($nextClass !== false) { $phpcsFile->recordMetric($stackPtr, 'File extension for class files', $extension); if ($extension === '.php') { $error = '%s found in ".php" file; use ".inc" extension instead'; $data = array(ucfirst($tokens[$nextClass]['content'])); $phpcsFile->addError($error, $stackPtr, 'ClassFound', $data); } } else { $phpcsFile->recordMetric($stackPtr, 'File extension for non-class files', $extension); if ($extension === '.inc') { $error = 'No interface or class found in ".inc" file; use ".php" extension instead'; $phpcsFile->addError($error, $stackPtr, 'NoClass'); } } // 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(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $prev = $phpcsFile->findPrevious(array(T_SEMICOLON, T_OPEN_TAG), $stackPtr - 1); if ($prev === false || $tokens[$prev]['code'] === T_OPEN_TAG) { $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']) { $phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'yes'); $error = 'Each PHP statement must be on a line by itself'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SameLine'); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); $phpcsFile->fixer->addNewline($prev); if ($tokens[$prev + 1]['code'] === T_WHITESPACE) { $phpcsFile->fixer->replaceToken($prev + 1, ''); } $phpcsFile->fixer->endChangeset(); } } else { $phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'no'); } }
/** * 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) { $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes'); $error = 'Short array syntax is not allowed'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found'); if ($fix === true) { $tokens = $phpcsFile->getTokens(); $opener = $tokens[$stackPtr]['bracket_opener']; $closer = $tokens[$stackPtr]['bracket_closer']; $phpcsFile->fixer->beginChangeset(); $phpcsFile->fixer->replaceToken($opener, 'array('); $phpcsFile->fixer->replaceToken($closer, ')'); $phpcsFile->fixer->endChangeset(); } }
/** * 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(File $phpcsFile, $stackPtr) { $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'); } $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(File $phpcsFile, $stackPtr) { $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'no'); $error = 'Short array syntax must be used to define arrays'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found'); if ($fix === true) { $tokens = $phpcsFile->getTokens(); $opener = $tokens[$stackPtr]['parenthesis_opener']; $closer = $tokens[$stackPtr]['parenthesis_closer']; $phpcsFile->fixer->beginChangeset(); if ($opener === null) { $phpcsFile->fixer->replaceToken($stackPtr, '[]'); } else { $phpcsFile->fixer->replaceToken($stackPtr, ''); $phpcsFile->fixer->replaceToken($opener, '['); $phpcsFile->fixer->replaceToken($closer, ']'); } $phpcsFile->fixer->endChangeset(); } }
/** * Processes this sniff, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile The current file being checked. * @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(); // Skip default values in function declarations. if ($tokens[$stackPtr]['code'] === T_EQUAL || $tokens[$stackPtr]['code'] === T_MINUS) { if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) { $parenthesis = array_keys($tokens[$stackPtr]['nested_parenthesis']); $bracket = array_pop($parenthesis); if (isset($tokens[$bracket]['parenthesis_owner']) === true) { $function = $tokens[$bracket]['parenthesis_owner']; if ($tokens[$function]['code'] === T_FUNCTION || $tokens[$function]['code'] === T_CLOSURE) { return; } } } } if ($tokens[$stackPtr]['code'] === T_EQUAL) { // Skip for '=&' case. if (isset($tokens[$stackPtr + 1]) === true && $tokens[$stackPtr + 1]['code'] === T_BITWISE_AND) { return; } } // Skip short ternary such as: "$foo = $bar ?: true;". if ($tokens[$stackPtr]['code'] === T_INLINE_THEN && $tokens[$stackPtr + 1]['code'] === T_INLINE_ELSE || $tokens[$stackPtr - 1]['code'] === T_INLINE_THEN && $tokens[$stackPtr]['code'] === T_INLINE_ELSE) { return; } if ($tokens[$stackPtr]['code'] === T_BITWISE_AND) { // If it's not a reference, then we expect one space either side of the // bitwise operator. if ($phpcsFile->isReference($stackPtr) === true) { return; } // Check there is one space before the & operator. if ($tokens[$stackPtr - 1]['code'] !== T_WHITESPACE) { $error = 'Expected 1 space before "&" operator; 0 found'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBeforeAmp'); if ($fix === true) { $phpcsFile->fixer->addContentBefore($stackPtr, ' '); } $phpcsFile->recordMetric($stackPtr, 'Space before operator', 0); } else { if ($tokens[$stackPtr - 2]['line'] !== $tokens[$stackPtr]['line']) { $found = 'newline'; } else { $found = $tokens[$stackPtr - 1]['length']; } $phpcsFile->recordMetric($stackPtr, 'Space before operator', $found); if ($found !== 1 && ($found !== 'newline' || $this->ignoreNewlines === false)) { $error = 'Expected 1 space before "&" operator; %s found'; $data = array($found); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeAmp', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr - 1, ' '); } } } //end if // Check there is one space after the & operator. if ($tokens[$stackPtr + 1]['code'] !== T_WHITESPACE) { $error = 'Expected 1 space after "&" operator; 0 found'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterAmp'); if ($fix === true) { $phpcsFile->fixer->addContent($stackPtr, ' '); } $phpcsFile->recordMetric($stackPtr, 'Space after operator', 0); } else { if ($tokens[$stackPtr + 2]['line'] !== $tokens[$stackPtr]['line']) { $found = 'newline'; } else { $found = $tokens[$stackPtr + 1]['length']; } $phpcsFile->recordMetric($stackPtr, 'Space after operator', $found); if ($found !== 1 && ($found !== 'newline' || $this->ignoreNewlines === false)) { $error = 'Expected 1 space after "&" operator; %s found'; $data = array($found); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterAmp', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr + 1, ' '); } } } //end if return; } //end if if ($tokens[$stackPtr]['code'] === T_MINUS || $tokens[$stackPtr]['code'] === T_PLUS) { // Check minus spacing, but make sure we aren't just assigning // a minus value or returning one. $prev = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); if ($tokens[$prev]['code'] === T_RETURN) { // Just returning a negative value; eg. (return -1). return; } if (isset(Tokens::$operators[$tokens[$prev]['code']]) === true) { // Just trying to operate on a negative value; eg. ($var * -1). return; } if (isset(Tokens::$comparisonTokens[$tokens[$prev]['code']]) === true) { // Just trying to compare a negative value; eg. ($var === -1). return; } if (isset(Tokens::$booleanOperators[$tokens[$prev]['code']]) === true) { // Just trying to compare a negative value; eg. ($var || -1 === $b). return; } if (isset(Tokens::$assignmentTokens[$tokens[$prev]['code']]) === true) { // Just trying to assign a negative value; eg. ($var = -1). return; } // A list of tokens that indicate that the token is not // part of an arithmetic operation. $invalidTokens = array(T_COMMA => true, T_OPEN_PARENTHESIS => true, T_OPEN_SQUARE_BRACKET => true, T_OPEN_SHORT_ARRAY => true, T_DOUBLE_ARROW => true, T_COLON => true, T_INLINE_THEN => true, T_INLINE_ELSE => true, T_CASE => true); if (isset($invalidTokens[$tokens[$prev]['code']]) === true) { // Just trying to use a negative value; eg. myFunction($var, -2). return; } } //end if $operator = $tokens[$stackPtr]['content']; if ($tokens[$stackPtr - 1]['code'] !== T_WHITESPACE) { $error = "Expected 1 space before \"{$operator}\"; 0 found"; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBefore'); if ($fix === true) { $phpcsFile->fixer->addContentBefore($stackPtr, ' '); } $phpcsFile->recordMetric($stackPtr, 'Space before operator', 0); } else { if (isset(Tokens::$assignmentTokens[$tokens[$stackPtr]['code']]) === false) { // Don't throw an error for assignments, because other standards allow // multiple spaces there to align multiple assignments. if ($tokens[$stackPtr - 2]['line'] !== $tokens[$stackPtr]['line']) { $found = 'newline'; } else { $found = $tokens[$stackPtr - 1]['length']; } $phpcsFile->recordMetric($stackPtr, 'Space before operator', $found); if ($found !== 1 && ($found !== 'newline' || $this->ignoreNewlines === false)) { $error = 'Expected 1 space before "%s"; %s found'; $data = array($operator, $found); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBefore', $data); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); if ($found === 'newline') { $i = $stackPtr - 2; while ($tokens[$i]['code'] === T_WHITESPACE) { $phpcsFile->fixer->replaceToken($i, ''); $i--; } } $phpcsFile->fixer->replaceToken($stackPtr - 1, ' '); $phpcsFile->fixer->endChangeset(); } } //end if } } //end if if (isset($tokens[$stackPtr + 1]) === false) { return; } if ($tokens[$stackPtr + 1]['code'] !== T_WHITESPACE) { $error = "Expected 1 space after \"{$operator}\"; 0 found"; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfter'); if ($fix === true) { $phpcsFile->fixer->addContent($stackPtr, ' '); } $phpcsFile->recordMetric($stackPtr, 'Space after operator', 0); } else { if (isset($tokens[$stackPtr + 2]) === true && $tokens[$stackPtr + 2]['line'] !== $tokens[$stackPtr]['line']) { $found = 'newline'; } else { $found = $tokens[$stackPtr + 1]['length']; } $phpcsFile->recordMetric($stackPtr, 'Space after operator', $found); if ($found !== 1 && ($found !== 'newline' || $this->ignoreNewlines === false)) { $error = 'Expected 1 space after "%s"; %s found'; $data = array($operator, $found); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfter', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr + 1, ' '); } } } //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(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 || $before === 'newline' && $this->ignoreNewlines === true) && ($after === $this->spacing || $after === 'newline' && $this->ignoreNewlines === true)) { 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 ($before !== 'newline' || $this->ignoreNewlines === false) { 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 ($after !== 'newline' || $this->ignoreNewlines === false) { 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(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]['parenthesis_opener']) === true && isset($tokens[$stackPtr]['parenthesis_closer']) === true) { $parenOpener = $tokens[$stackPtr]['parenthesis_opener']; $parenCloser = $tokens[$stackPtr]['parenthesis_closer']; if ($tokens[$parenOpener + 1]['code'] === T_WHITESPACE) { $gap = $tokens[$parenOpener + 1]['length']; if ($gap === 0) { $phpcsFile->recordMetric($stackPtr, 'Spaces after control structure open parenthesis', 'newline'); $gap = 'newline'; } else { $phpcsFile->recordMetric($stackPtr, 'Spaces after control structure open parenthesis', $gap); } $error = 'Expected 0 spaces after opening bracket; %s found'; $data = array($gap); $fix = $phpcsFile->addFixableError($error, $parenOpener + 1, 'SpacingAfterOpenBrace', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($parenOpener + 1, ''); } } else { $phpcsFile->recordMetric($stackPtr, 'Spaces after control structure open parenthesis', 0); } if ($tokens[$parenOpener]['line'] === $tokens[$parenCloser]['line'] && $tokens[$parenCloser - 1]['code'] === T_WHITESPACE) { $gap = $tokens[$parenCloser - 1]['length']; $error = 'Expected 0 spaces before closing bracket; %s found'; $data = array($gap); $fix = $phpcsFile->addFixableError($error, $parenCloser - 1, 'SpaceBeforeCloseBrace', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($parenCloser - 1, ''); } if ($gap === 0) { $phpcsFile->recordMetric($stackPtr, 'Spaces before control structure close parenthesis', 'newline'); } else { $phpcsFile->recordMetric($stackPtr, 'Spaces before control structure close parenthesis', $gap); } } else { $phpcsFile->recordMetric($stackPtr, 'Spaces before control structure close parenthesis', 0); } } //end if if (isset($tokens[$stackPtr]['scope_closer']) === false) { return; } $scopeOpener = $tokens[$stackPtr]['scope_opener']; $scopeCloser = $tokens[$stackPtr]['scope_closer']; for ($firstContent = $scopeOpener + 1; $firstContent < $phpcsFile->numTokens; $firstContent++) { if ($tokens[$firstContent]['code'] !== T_WHITESPACE) { break; } } // We ignore spacing for some structures that tend to have their own rules. $ignore = array(T_FUNCTION => true, T_CLASS => true, T_INTERFACE => true, T_TRAIT => true, T_DOC_COMMENT_OPEN_TAG => true); if (isset($ignore[$tokens[$firstContent]['code']]) === false && $tokens[$firstContent]['line'] >= $tokens[$scopeOpener]['line'] + 2) { $error = 'Blank line found at start of control structure'; $fix = $phpcsFile->addFixableError($error, $scopeOpener, 'SpacingAfterOpen'); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); $i = $scopeOpener + 1; while ($tokens[$i]['line'] !== $tokens[$firstContent]['line']) { $phpcsFile->fixer->replaceToken($i, ''); $i++; } $phpcsFile->fixer->addNewline($scopeOpener); $phpcsFile->fixer->endChangeset(); } } if ($firstContent !== $scopeCloser) { $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, $scopeCloser - 1, null, true); $lastNonEmptyContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, $scopeCloser - 1, null, true); $checkToken = $lastContent; if (isset($tokens[$lastNonEmptyContent]['scope_condition']) === true) { $checkToken = $tokens[$lastNonEmptyContent]['scope_condition']; } if (isset($ignore[$tokens[$checkToken]['code']]) === false && $tokens[$lastContent]['line'] <= $tokens[$scopeCloser]['line'] - 2) { $errorToken = $scopeCloser; for ($i = $scopeCloser - 1; $i > $lastContent; $i--) { if ($tokens[$i]['line'] < $tokens[$scopeCloser]['line']) { $errorToken = $i; break; } } $error = 'Blank line found at end of control structure'; $fix = $phpcsFile->addFixableError($error, $errorToken, 'SpacingBeforeClose'); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); $i = $scopeCloser - 1; for ($i = $scopeCloser - 1; $i > $lastContent; $i--) { if ($tokens[$i]['line'] === $tokens[$scopeCloser]['line']) { continue; } if ($tokens[$i]['line'] === $tokens[$lastContent]['line']) { break; } $phpcsFile->fixer->replaceToken($i, ''); } $phpcsFile->fixer->endChangeset(); } } //end if } //end if $trailingContent = $phpcsFile->findNext(T_WHITESPACE, $scopeCloser + 1, null, true); if ($tokens[$trailingContent]['code'] === T_COMMENT) { // Special exception for code where the comment about // an ELSE or ELSEIF is written between the control structures. $nextCode = $phpcsFile->findNext(Tokens::$emptyTokens, $scopeCloser + 1, null, true); if ($tokens[$nextCode]['code'] === T_ELSE || $tokens[$nextCode]['code'] === T_ELSEIF) { $trailingContent = $nextCode; } } //end if if ($tokens[$trailingContent]['code'] === T_ELSE) { if ($tokens[$stackPtr]['code'] === T_IF) { // IF with ELSE. return; } } if ($tokens[$trailingContent]['code'] === T_WHILE && $tokens[$stackPtr]['code'] === T_DO) { // DO with WHILE. return; } if ($tokens[$trailingContent]['code'] === T_CLOSE_TAG) { // At the end of the script or embedded code. return; } if (isset($tokens[$trailingContent]['scope_condition']) === true && $tokens[$trailingContent]['scope_condition'] !== $trailingContent && isset($tokens[$trailingContent]['scope_opener']) === true && $tokens[$trailingContent]['scope_opener'] !== $trailingContent) { // Another control structure's closing brace. $owner = $tokens[$trailingContent]['scope_condition']; if ($tokens[$owner]['code'] === T_FUNCTION) { // The next content is the closing brace of a function // so normal function rules apply and we can ignore it. return; } if ($tokens[$owner]['code'] === T_CLOSURE && ($phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true || $phpcsFile->hasCondition($stackPtr, T_CLOSURE) === true || isset($tokens[$stackPtr]['nested_parenthesis']) === true)) { return; } if ($tokens[$trailingContent]['line'] !== $tokens[$scopeCloser]['line'] + 1) { $error = 'Blank line found after control structure'; $fix = $phpcsFile->addFixableError($error, $scopeCloser, 'LineAfterClose'); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); $i = $scopeCloser + 1; while ($tokens[$i]['line'] !== $tokens[$trailingContent]['line']) { $phpcsFile->fixer->replaceToken($i, ''); $i++; } $phpcsFile->fixer->addNewline($scopeCloser); $phpcsFile->fixer->endChangeset(); } } } else { if ($tokens[$trailingContent]['code'] !== T_ELSE && $tokens[$trailingContent]['code'] !== T_ELSEIF && $tokens[$trailingContent]['code'] !== T_CATCH && $tokens[$trailingContent]['line'] === $tokens[$scopeCloser]['line'] + 1) { $error = 'No blank line found after control structure'; $fix = $phpcsFile->addFixableError($error, $scopeCloser, 'NoLineAfterClose'); if ($fix === true) { $phpcsFile->fixer->addNewline($scopeCloser); } } } //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 int */ public function process(File $phpcsFile, $stackPtr) { $this->currentFile = $phpcsFile; $tokens = $phpcsFile->getTokens(); $commentStart = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); if ($tokens[$commentStart]['code'] === T_COMMENT) { $phpcsFile->addError('You must use "/**" style comments for a file comment', $commentStart, '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', $stackPtr, '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'); // No blank line between the open tag and the file comment. if ($tokens[$commentStart]['line'] > $tokens[$stackPtr]['line'] + 1) { $error = 'There must be no blank lines before the file comment'; $phpcsFile->addError($error, $stackPtr, 'SpacingAfterOpen'); } // Exactly one blank line after the file comment. $next = $phpcsFile->findNext(T_WHITESPACE, $commentEnd + 1, null, true); if ($tokens[$next]['line'] !== $tokens[$commentEnd]['line'] + 2) { $error = 'There must be exactly one blank line after the file comment'; $phpcsFile->addError($error, $commentEnd, 'SpacingAfterComment'); } // Required tags in correct order. $required = array('@package' => true, '@subpackage' => true, '@author' => true, '@copyright' => true); $foundTags = array(); foreach ($tokens[$commentStart]['comment_tags'] as $tag) { $name = $tokens[$tag]['content']; $isRequired = isset($required[$name]); if ($isRequired === true && in_array($name, $foundTags) === true) { $error = 'Only one %s tag is allowed in a file comment'; $data = array($name); $phpcsFile->addError($error, $tag, 'Duplicate' . ucfirst(substr($name, 1)) . 'Tag', $data); } $foundTags[] = $name; if ($isRequired === false) { continue; } $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd); if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) { $error = 'Content missing for %s tag in file comment'; $data = array($name); $phpcsFile->addError($error, $tag, 'Empty' . ucfirst(substr($name, 1)) . 'Tag', $data); continue; } if ($name === '@author') { if ($tokens[$string]['content'] !== 'Squiz Pty Ltd <*****@*****.**>') { $error = 'Expected "Squiz Pty Ltd <*****@*****.**>" for author tag'; $fix = $phpcsFile->addFixableError($error, $tag, 'IncorrectAuthor'); if ($fix === true) { $expected = 'Squiz Pty Ltd <*****@*****.**>'; $phpcsFile->fixer->replaceToken($string, $expected); } } } else { if ($name === '@copyright') { if (preg_match('/^([0-9]{4})(-[0-9]{4})? (Squiz Pty Ltd \\(ABN 77 084 670 600\\))$/', $tokens[$string]['content']) === 0) { $error = 'Expected "xxxx-xxxx Squiz Pty Ltd (ABN 77 084 670 600)" for copyright declaration'; $fix = $phpcsFile->addFixableError($error, $tag, 'IncorrectCopyright'); if ($fix === true) { $matches = array(); preg_match('/^(([0-9]{4})(-[0-9]{4})?)?.*$/', $tokens[$string]['content'], $matches); if (isset($matches[1]) === false) { $matches[1] = date('Y'); } $expected = $matches[1] . ' Squiz Pty Ltd (ABN 77 084 670 600)'; $phpcsFile->fixer->replaceToken($string, $expected); } } } } //end if } //end foreach // Check if the tags are in the correct position. $pos = 0; foreach ($required as $tag => $true) { if (in_array($tag, $foundTags) === false) { $error = 'Missing %s tag in file comment'; $data = array($tag); $phpcsFile->addError($error, $commentEnd, 'Missing' . ucfirst(substr($tag, 1)) . 'Tag', $data); } if (isset($foundTags[$pos]) === false) { break; } if ($foundTags[$pos] !== $tag) { $error = 'The tag in position %s should be the %s tag'; $data = array($pos + 1, $tag); $phpcsFile->addError($error, $tokens[$commentStart]['comment_tags'][$pos], ucfirst(substr($tag, 1)) . 'TagOrder', $data); } $pos++; } //end foreach // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }