/** * 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(); // We are only interested in the first token in a multi-line string. if ($tokens[$stackPtr]['code'] === $tokens[$stackPtr - 1]['code']) { return; } $workingString = $tokens[$stackPtr]['content']; $lastStringToken = $stackPtr; $i = $stackPtr + 1; if (isset($tokens[$i]) === true) { while ($i < $phpcsFile->numTokens && $tokens[$i]['code'] === $tokens[$stackPtr]['code']) { $workingString .= $tokens[$i]['content']; $lastStringToken = $i; $i++; } } // Check if it's a double quoted string. if (strpos($workingString, '"') === false) { return; } // Make sure it's not a part of a string started in a previous line. // If it is, then we have already checked it. if ($workingString[0] !== '"') { return; } // The use of variables in double quoted strings is not allowed. if ($tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING) { $stringTokens = token_get_all('<?php ' . $workingString); foreach ($stringTokens as $token) { if (is_array($token) === true && $token[0] === T_VARIABLE) { $error = 'Variable "%s" not allowed in double quoted string; use concatenation instead'; $data = array($token[1]); $phpcsFile->addError($error, $stackPtr, 'ContainsVar', $data); } } return; } //end if $allowedChars = array('\\0', '\\1', '\\2', '\\3', '\\4', '\\5', '\\6', '\\7', '\\n', '\\r', '\\f', '\\t', '\\v', '\\x', '\\b', '\\e', '\\u', '\''); foreach ($allowedChars as $testChar) { if (strpos($workingString, $testChar) !== false) { return; } } $error = 'String %s does not require double quotes; use single quotes instead'; $data = array(str_replace("\n", '\\n', $workingString)); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotRequired', $data); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); $innerContent = substr($workingString, 1, -1); $innerContent = str_replace('\\"', '"', $innerContent); $phpcsFile->fixer->replaceToken($stackPtr, "'{$innerContent}'"); while ($lastStringToken !== $stackPtr) { $phpcsFile->fixer->replaceToken($lastStringToken, ''); $lastStringToken--; } $phpcsFile->fixer->endChangeset(); } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(File $phpcsFile, $stackPtr) { $csslintPath = Config::getExecutablePath('csslint'); if ($csslintPath === null) { return; } $fileName = $phpcsFile->getFilename(); $cmd = $csslintPath . ' ' . escapeshellarg($fileName); exec($cmd, $output, $retval); if (is_array($output) === false) { return; } $count = count($output); for ($i = 0; $i < $count; $i++) { $matches = array(); $numMatches = preg_match('/(error|warning) at line (\\d+)/', $output[$i], $matches); if ($numMatches === 0) { continue; } $line = (int) $matches[2]; $message = 'csslint says: ' . $output[$i + 1]; // First line is message with error line and error code. // Second is error message. // Third is wrong line in file. // Fourth is empty line. $i += 4; $phpcsFile->addWarningOnLine($message, $line, 'ExternalTool'); } //end for // 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(); $token = $tokens[$stackPtr]; // Skip invalid statement. if (isset($token['parenthesis_opener']) === false) { return; } $next = ++$token['parenthesis_opener']; $end = --$token['parenthesis_closer']; $parts = array(0, 0, 0); $index = 0; for (; $next <= $end; ++$next) { $code = $tokens[$next]['code']; if ($code === T_SEMICOLON) { ++$index; } else { if (isset(Tokens::$emptyTokens[$code]) === false) { ++$parts[$index]; } } } if ($parts[0] === 0 && $parts[2] === 0 && $parts[1] > 0) { $error = 'This FOR loop can be simplified to a WHILE loop'; $phpcsFile->addWarning($error, $stackPtr, 'CanSimplify'); } }
/** * Processes class member variables. * * @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 */ protected function processMemberVar(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $memberProps = $phpcsFile->getMemberProperties($stackPtr); if (empty($memberProps) === true) { return; } $memberName = ltrim($tokens[$stackPtr]['content'], '$'); $scope = $memberProps['scope']; $scopeSpecified = $memberProps['scope_specified']; if ($memberProps['scope'] === 'private') { $isPublic = false; } else { $isPublic = true; } // If it's a private member, it must have an underscore on the front. if ($isPublic === false && $memberName[0] !== '_') { $error = 'Private member variable "%s" must be prefixed with an underscore'; $data = array($memberName); $phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $data); return; } // If it's not a private member, it must not have an underscore on the front. if ($isPublic === true && $scopeSpecified === true && $memberName[0] === '_') { $error = '%s member variable "%s" must not be prefixed with an underscore'; $data = array(ucfirst($scope), $memberName); $phpcsFile->addError($error, $stackPtr, 'PublicUnderscore', $data); return; } }
/** * 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]['scope_opener']) === false) { // Probably an interface method. return; } $openBrace = $tokens[$stackPtr]['scope_opener']; $nextContent = $phpcsFile->findNext(T_WHITESPACE, $openBrace + 1, null, true); if ($nextContent === $tokens[$stackPtr]['scope_closer']) { // The next bit of content is the closing brace, so this // is an empty function and should have a blank line // between the opening and closing braces. return; } $braceLine = $tokens[$openBrace]['line']; $nextLine = $tokens[$nextContent]['line']; $found = $nextLine - $braceLine - 1; if ($found > 0) { $error = 'Expected 0 blank lines after opening function brace; %s found'; $data = array($found); $fix = $phpcsFile->addFixableError($error, $openBrace, 'SpacingAfter', $data); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); for ($i = $openBrace + 1; $i < $nextContent; $i++) { if ($tokens[$i]['line'] === $nextLine) { break; } $phpcsFile->fixer->replaceToken($i, ''); } $phpcsFile->fixer->addNewline($openBrace); $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(); // Ignore abstract methods. if (isset($tokens[$stackPtr]['scope_opener']) === false) { return; } // Detect start and end of this function definition. $start = $tokens[$stackPtr]['scope_opener']; $end = $tokens[$stackPtr]['scope_closer']; $nestingLevel = 0; // Find the maximum nesting level of any token in the function. for ($i = $start + 1; $i < $end; $i++) { $level = $tokens[$i]['level']; if ($nestingLevel < $level) { $nestingLevel = $level; } } // We subtract the nesting level of the function itself. $nestingLevel = $nestingLevel - $tokens[$stackPtr]['level'] - 1; if ($nestingLevel > $this->absoluteNestingLevel) { $error = 'Function\'s nesting level (%s) exceeds allowed maximum of %s'; $data = array($nestingLevel, $this->absoluteNestingLevel); $phpcsFile->addError($error, $stackPtr, 'MaxExceeded', $data); } else { if ($nestingLevel > $this->nestingLevel) { $warning = 'Function\'s nesting level (%s) exceeds %s; consider refactoring the function'; $data = array($nestingLevel, $this->nestingLevel); $phpcsFile->addWarning($warning, $stackPtr, 'TooHigh', $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(); $prevType = $tokens[$stackPtr - 1]['code']; if (isset(Tokens::$emptyTokens[$prevType]) === false) { return; } $nonSpace = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 2, null, true); if ($tokens[$nonSpace]['code'] === T_SEMICOLON) { // Empty statement. return; } $expected = $tokens[$nonSpace]['content'] . ';'; $found = $phpcsFile->getTokensAsString($nonSpace, $stackPtr - $nonSpace) . ';'; $error = 'Space found before semicolon; expected "%s" but found "%s"'; $data = array($expected, $found); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Incorrect', $data); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); for ($i = $stackPtr - 1; $i > $nonSpace; $i--) { $phpcsFile->fixer->replaceToken($i, ''); } $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) { $fileName = $phpcsFile->getFilename(); $matches = array(); if (preg_match('|/systems/(.*)/([^/]+)?actions.inc$|i', $fileName, $matches) === 0) { // Not an actions file. return; } $ownClass = $matches[2]; $tokens = $phpcsFile->getTokens(); $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $stackPtr + 2, null, false, true); $typeName = trim($tokens[$typeName]['content'], " '"); switch (strtolower($tokens[$stackPtr + 1]['content'])) { case 'includesystem': $included = strtolower($typeName); break; case 'includeasset': $included = strtolower($typeName) . 'assettype'; break; case 'includewidget': $included = strtolower($typeName) . 'widgettype'; break; default: return; } if ($included === strtolower($ownClass)) { $error = "You do not need to include \"%s\" from within the system's own actions file"; $data = array($ownClass); $phpcsFile->addError($error, $stackPtr, 'NotRequired', $data); } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); // Find the content of each style definition name. $styleNames = array(); $next = $stackPtr; $end = $tokens[$stackPtr]['bracket_closer']; do { $next = $phpcsFile->findNext(array(T_STYLE, T_OPEN_CURLY_BRACKET), $next + 1, $end); if ($next === false) { // Class definition is empty. break; } if ($tokens[$next]['code'] === T_OPEN_CURLY_BRACKET) { $next = $tokens[$next]['bracket_closer']; continue; } $name = $tokens[$next]['content']; if (isset($styleNames[$name]) === true) { $first = $styleNames[$name]; $error = 'Duplicate style definition found; first defined on line %s'; $data = array($tokens[$first]['line']); $phpcsFile->addError($error, $next, 'Found', $data); } else { $styleNames[$name] = $next; } } while ($next !== false); }
/** * 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 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(); $token = $tokens[$stackPtr]; // Skip for-loop without body. if (isset($token['scope_opener']) === false) { return; } // Find incrementors for outer loop. $outer = $this->findIncrementers($tokens, $token); // Skip if empty. if (count($outer) === 0) { return; } // Find nested for loops. $start = ++$token['scope_opener']; $end = --$token['scope_closer']; for (; $start <= $end; ++$start) { if ($tokens[$start]['code'] !== T_FOR) { continue; } $inner = $this->findIncrementers($tokens, $tokens[$start]); $diff = array_intersect($outer, $inner); if (count($diff) !== 0) { $error = 'Loop incrementor (%s) jumbling with inner loop'; $data = array(join(', ', $diff)); $phpcsFile->addWarning($error, $stackPtr, 'Found', $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(); if ($tokens[$stackPtr]['content'] === 'ob_end_flush') { $phpcsFile->addError('Use of ob_end_flush() is not allowed; use ob_get_contents() and ob_end_clean() instead', $stackPtr, 'Found'); } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. * * @return void */ public function process(File $phpcsFile, $stackPtr) { $this->currentFile = $phpcsFile; $tokens = $phpcsFile->getTokens(); // Ignore abstract methods. if (isset($tokens[$stackPtr]['scope_opener']) === false) { return; } // Detect start and end of this function definition. $start = $tokens[$stackPtr]['scope_opener']; $end = $tokens[$stackPtr]['scope_closer']; // Predicate nodes for PHP. $find = array(T_CASE => true, T_DEFAULT => true, T_CATCH => true, T_IF => true, T_FOR => true, T_FOREACH => true, T_WHILE => true, T_DO => true, T_ELSEIF => true); $complexity = 1; // Iterate from start to end and count predicate nodes. for ($i = $start + 1; $i < $end; $i++) { if (isset($find[$tokens[$i]['code']]) === true) { $complexity++; } } if ($complexity > $this->absoluteComplexity) { $error = 'Function\'s cyclomatic complexity (%s) exceeds allowed maximum of %s'; $data = array($complexity, $this->absoluteComplexity); $phpcsFile->addError($error, $stackPtr, 'MaxExceeded', $data); } else { if ($complexity > $this->complexity) { $warning = 'Function\'s cyclomatic complexity (%s) exceeds %s; consider refactoring the function'; $data = array($complexity, $this->complexity); $phpcsFile->addWarning($warning, $stackPtr, 'TooHigh', $data); } } }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile The current file being scanned. * @param int $stackPtr The position of the current token in the * stack passed in $tokens. * @param int $currScope A pointer to the start of the scope. * * @return void */ public function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope) { $tokens = $phpcsFile->getTokens(); $function = $tokens[$stackPtr + 2]; if ($function['code'] !== T_STRING) { return; } $functionName = $function['content']; $classOpener = $tokens[$currScope]['scope_condition']; $className = $tokens[$classOpener + 2]['content']; $methodProps = $phpcsFile->getMethodProperties($stackPtr); if ($methodProps['is_static'] === true) { if (isset($tokens[$stackPtr]['scope_closer']) === false) { // There is no scope opener or closer, so the function // must be abstract. return; } $thisUsage = $stackPtr; while (($thisUsage = $phpcsFile->findNext(array(T_VARIABLE), $thisUsage + 1, $tokens[$stackPtr]['scope_closer'], false, '$this')) !== false) { if ($thisUsage === false) { return; } $error = 'Usage of "$this" in static methods will cause runtime errors'; $phpcsFile->addError($error, $thisUsage, 'Found'); } } //end if }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void * @throws PHP_CodeSniffer_Exception If jslint.js could not be run */ public function process(File $phpcsFile, $stackPtr) { $rhinoPath = Config::getExecutablePath('jslint'); $jslintPath = Config::getExecutablePath('jslint'); if ($rhinoPath === null || $jslintPath === null) { return; } $fileName = $phpcsFile->getFilename(); $cmd = "{$rhinoPath} \"{$jslintPath}\" \"{$fileName}\""; $msg = exec($cmd, $output, $retval); if (is_array($output) === true) { foreach ($output as $finding) { $matches = array(); $numMatches = preg_match('/Lint at line ([0-9]+).*:(.*)$/', $finding, $matches); if ($numMatches === 0) { continue; } $line = (int) $matches[1]; $message = 'jslint says: ' . trim($matches[2]); $phpcsFile->addWarningOnLine($message, $line, 'ExternalTool'); } } // 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(); $token = $tokens[$stackPtr]; // Skip for-loop without body. if (isset($token['parenthesis_opener']) === false) { return; } $next = ++$token['parenthesis_opener']; $end = --$token['parenthesis_closer']; $goodCondition = false; for (; $next <= $end; ++$next) { $code = $tokens[$next]['code']; if (isset(Tokens::$emptyTokens[$code]) === true) { continue; } else { if ($code !== T_TRUE && $code !== T_FALSE) { $goodCondition = true; } } } if ($goodCondition === false) { $error = 'Avoid IF statements that are always true or false'; $phpcsFile->addWarning($error, $stackPtr, 'Found'); } }
/** * 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(); if (substr($tokens[$stackPtr]['content'], 0, 2) !== '//') { return; } $commentLine = $tokens[$stackPtr]['line']; $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); if ($tokens[$lastContent]['line'] !== $commentLine) { return; } if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) { return; } // Special case for JS files. if ($tokens[$lastContent]['code'] === T_COMMA || $tokens[$lastContent]['code'] === T_SEMICOLON) { $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, $lastContent - 1, null, true); if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) { return; } } $error = 'Comments may not appear after statements'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found'); if ($fix === true) { $phpcsFile->fixer->addNewlineBefore($stackPtr); } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(File $phpcsFile, $stackPtr) { $jslPath = Config::getExecutablePath('jsl'); if (is_null($jslPath) === true) { return; } $fileName = $phpcsFile->getFilename(); $cmd = '"' . $jslPath . '" -nologo -nofilelisting -nocontext -nosummary -output-format __LINE__:__ERROR__ -process "' . $fileName . '"'; $msg = exec($cmd, $output, $retval); // Variable $exitCode is the last line of $output if no error occurs, on // error it is numeric. Try to handle various error conditions and // provide useful error reporting. if ($retval === 2 || $retval === 4) { if (is_array($output) === true) { $msg = join('\\n', $output); } throw new RuntimeException("Failed invoking JavaScript Lint, retval was [{$retval}], output was [{$msg}]"); } if (is_array($output) === true) { foreach ($output as $finding) { $split = strpos($finding, ':'); $line = substr($finding, 0, $split); $message = substr($finding, $split + 1); $phpcsFile->addWarningOnLine(trim($message), $line, 'ExternalTool'); } } // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $lastLine = $tokens[$stackPtr]['line']; $end = $tokens[$stackPtr]['bracket_closer']; $endLine = $tokens[$end]['line']; // Do not check nested style definitions as, for example, in @media style rules. $nested = $phpcsFile->findNext(T_OPEN_CURLY_BRACKET, $stackPtr + 1, $end); if ($nested !== false) { return; } $foundColon = false; $foundString = false; for ($i = $stackPtr + 1; $i <= $end; $i++) { if ($tokens[$i]['line'] !== $lastLine) { // We changed lines. if ($foundColon === false && $foundString !== false) { // We didn't find a colon on the previous line. $error = 'No style definition found on line; check for missing colon'; $phpcsFile->addError($error, $foundString, 'Found'); } $foundColon = false; $foundString = false; $lastLine = $tokens[$i]['line']; } if ($tokens[$i]['code'] === T_STRING) { $foundString = $i; } else { if ($tokens[$i]['code'] === T_COLON) { $foundColon = $i; } } } //end for }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $colour = $tokens[$stackPtr]['content']; $expected = strtoupper($colour); if ($colour !== $expected) { $error = 'CSS colours must be defined in uppercase; expected %s but found %s'; $data = array($expected, $colour); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotUpper', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr, $expected); } } // Now check if shorthand can be used. if (strlen($colour) !== 7) { return; } if ($colour[1] === $colour[2] && $colour[3] === $colour[4] && $colour[5] === $colour[6]) { $expected = '#' . $colour[1] . $colour[3] . $colour[5]; $error = 'CSS colours must use shorthand if available; expected %s but found %s'; $data = array($expected, $colour); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Shorthand', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr, $expected); } } }
/** * 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) { if ($this->phpPath === null) { $this->phpPath = Config::getExecutablePath('php'); if ($this->phpPath === null) { // PHP_BINARY is available in PHP 5.4+. if (defined('PHP_BINARY') === true) { $this->phpPath = PHP_BINARY; } else { return; } } } $fileName = $phpcsFile->getFilename(); $cmd = $this->phpPath . " -l \"{$fileName}\" 2>&1"; $output = shell_exec($cmd); $matches = array(); if (preg_match('/^.*error:(.*) in .* on line ([0-9]+)/', trim($output), $matches) === 1) { $error = trim($matches[1]); $line = (int) $matches[2]; $phpcsFile->addErrorOnLine("PHP syntax error: {$error}", $line, 'PHPSyntax'); } // 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 + 1]['code'] === T_SEMICOLON) { // No content for this language construct. return; } if ($tokens[$stackPtr + 1]['code'] === T_WHITESPACE) { $content = $tokens[$stackPtr + 1]['content']; $contentLength = strlen($content); if ($contentLength !== 1) { $error = 'Language constructs must be followed by a single space; expected 1 space but found %s'; $data = array($contentLength); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'IncorrectSingle', $data); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr + 1, ' '); } } } else { if ($tokens[$stackPtr + 1]['code'] !== T_OPEN_PARENTHESIS) { $error = 'Language constructs must be followed by a single space; expected "%s" but found "%s"'; $data = array($tokens[$stackPtr]['content'] . ' ' . $tokens[$stackPtr + 1]['content'], $tokens[$stackPtr]['content'] . $tokens[$stackPtr + 1]['content']); $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Incorrect', $data); if ($fix === true) { $phpcsFile->fixer->addContent($stackPtr, ' '); } } } //end if }
/** * Processes the function tokens within the class. * * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found. * @param int $stackPtr The position where the token was found. * @param int $currScope The current scope opener token. * * @return void */ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope) { $tokens = $phpcsFile->getTokens(); $methodName = $phpcsFile->getDeclarationName($stackPtr); if ($methodName === null) { // Ignore closures. return; } if ($phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true) { // Ignore nested functions. return; } $modifier = null; for ($i = $stackPtr - 1; $i > 0; $i--) { if ($tokens[$i]['line'] < $tokens[$stackPtr]['line']) { break; } else { if (isset(Tokens::$scopeModifiers[$tokens[$i]['code']]) === true) { $modifier = $i; break; } } } if ($modifier === null) { $error = 'Visibility must be declared on method "%s"'; $data = array($methodName); $phpcsFile->addError($error, $stackPtr, 'Missing', $data); } }
/** * Generate a partial report for a single processed file. * * Function should return TRUE if it printed or stored data about the file * and FALSE if it ignored the file. Returning TRUE indicates that the file and * its data should be counted in the grand totals. * * @param array $report Prepared report data. * @param \PHP_CodeSniffer\File $phpcsFile The file being reported on. * @param bool $showSources Show sources? * @param int $width Maximum allowed line width. * * @return bool */ public function generateFileReport($report, File $phpcsFile, $showSources = false, $width = 80) { $errors = $phpcsFile->getFixableCount(); if ($errors !== 0) { if (PHP_CODESNIFFER_VERBOSITY > 0) { ob_end_clean(); $startTime = microtime(true); echo "\t=> Fixing file: {$errors}/{$errors} violations remaining"; } $fixed = $phpcsFile->fixer->fixFile(); } if ($phpcsFile->config->stdin === true) { // Replacing STDIN, so output current file to STDOUT // even if nothing was fixed. Exit here because we // can't process any more than 1 file in this setup. echo $phpcsFile->fixer->getContents(); ob_end_flush(); exit(1); } if ($errors === 0) { return false; } if (PHP_CODESNIFFER_VERBOSITY > 0) { if ($fixed === false) { echo 'ERROR'; } else { echo 'DONE'; } $timeTaken = (microtime(true) - $startTime) * 1000; if ($timeTaken < 1000) { $timeTaken = round($timeTaken); echo " in {$timeTaken}ms" . PHP_EOL; } else { $timeTaken = round($timeTaken / 1000, 2); echo " in {$timeTaken} secs" . PHP_EOL; } } if ($fixed === true) { $newFilename = $report['filename'] . $phpcsFile->config->suffix; $newContent = $phpcsFile->fixer->getContents(); file_put_contents($newFilename, $newContent); if (PHP_CODESNIFFER_VERBOSITY > 0) { if ($newFilename === $report['filename']) { echo "\t=> File was overwritten" . PHP_EOL; } else { echo "\t=> Fixed file written to " . basename($newFilename) . PHP_EOL; } } } if (PHP_CODESNIFFER_VERBOSITY > 0) { ob_start(); } $errorCount = $phpcsFile->getErrorCount(); $warningCount = $phpcsFile->getWarningCount(); $fixableCount = $phpcsFile->getFixableCount(); $fixedCount = $errors - $fixableCount; echo $report['filename'] . ">>{$errorCount}>>{$warningCount}>>{$fixableCount}>>{$fixedCount}" . PHP_EOL; return $fixed; }
/** * 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) { $nextClass = $phpcsFile->findNext($this->register(), $stackPtr + 1); if ($nextClass !== false) { $error = 'Only one class is allowed in a file'; $phpcsFile->addError($error, $nextClass, 'MultipleFound'); } }
/** * 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 (strtolower($tokens[$stackPtr]['content']) === 'console') { $error = 'Variables, functions and labels must not be named "console"; name may conflict with Firebug internal variable'; $phpcsFile->addError($error, $stackPtr, 'ConflictFound'); } }
/** * 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) { $closeTag = $phpcsFile->findNext(T_CLOSE_TAG, $stackPtr); if ($closeTag === false) { $error = 'The PHP open tag does not have a corresponding PHP close tag'; $phpcsFile->addError($error, $stackPtr, 'NotFound'); } }
/** * 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) { $error = 'Usage of ELSEIF not allowed; use ELSE IF instead'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotAllowed'); if ($fix === true) { $phpcsFile->fixer->replaceToken($stackPtr, 'else 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->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 }