/** * 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) { 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 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) { $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) { $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) { $fullPath = basename($phpcsFile->getFilename()); $fileName = substr($fullPath, 0, strrpos($fullPath, '.')); if ($fileName === '') { // No filename probably means STDIN, so we can't do this check. return; } $tokens = $phpcsFile->getTokens(); $decName = $phpcsFile->findNext(T_STRING, $stackPtr); if ($tokens[$decName]['content'] !== $fileName) { $error = '%s name doesn\'t match filename; expected "%s %s"'; $data = array(ucfirst($tokens[$stackPtr]['content']), $tokens[$stackPtr]['content'], $fileName); $phpcsFile->addError($error, $stackPtr, 'NoMatch', $data); } }
/** * Generates a text diff of the original file and the new content. * * @param string $filePath Optional file path to diff the file against. * If not specified, the original version of the * file will be used. * @param boolean $colors Print colored output or not. * * @return string */ public function generateDiff($filePath = null, $colors = true) { if ($filePath === null) { $filePath = $this->currentFile->getFilename(); } $cwd = getcwd() . DIRECTORY_SEPARATOR; $filename = str_replace($cwd, '', $filePath); $contents = $this->getContents(); $tempName = tempnam(sys_get_temp_dir(), 'phpcs-fixer'); $fixedFile = fopen($tempName, 'w'); fwrite($fixedFile, $contents); // We must use something like shell_exec() because whitespace at the end // of lines is critical to diff files. $cmd = "diff -u -L\"{$filename}\" -LPHP_CodeSniffer \"{$filename}\" \"{$tempName}\""; $diff = shell_exec($cmd); fclose($fixedFile); if (is_file($tempName) === true) { unlink($tempName); } if ($colors === false) { return $diff; } $diffLines = explode(PHP_EOL, $diff); if (count($diffLines) === 1) { // Seems to be required for cygwin. $diffLines = explode("\n", $diff); } $diff = array(); foreach ($diffLines as $line) { if (isset($line[0]) === true) { switch ($line[0]) { case '-': $diff[] = "[31m{$line}[0m"; break; case '+': $diff[] = "[32m{$line}[0m"; break; default: $diff[] = $line; } } } $diff = implode(PHP_EOL, $diff); return $diff; }
/** * 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) { // Ignore files with browser-specific suffixes. $filename = $phpcsFile->getFilename(); $breakChar = strrpos($filename, '_'); if ($breakChar !== false && substr($filename, -4) === '.css') { $specific = substr($filename, $breakChar + 1, -4); if (isset($this->specificStylesheets[$specific]) === true) { return; } } $tokens = $phpcsFile->getTokens(); $content = $tokens[$stackPtr]['content']; if ($content[0] === '-') { $error = 'Browser-specific styles are not allowed'; $phpcsFile->addError($error, $stackPtr, 'ForbiddenStyle'); } }
/** * 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 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 int */ public function process(File $phpcsFile, $stackPtr) { $analyzerPath = Config::getExecutablePath('zend_ca'); if (is_null($analyzerPath) === true) { return; } $fileName = $phpcsFile->getFilename(); // In the command, 2>&1 is important because the code analyzer sends its // findings to stderr. $output normally contains only stdout, so using 2>&1 // will pipe even stderr to stdout. $cmd = $analyzerPath . ' ' . $fileName . ' 2>&1'; // There is the possibility to pass "--ide" as an option to the analyzer. // This would result in an output format which would be easier to parse. // The problem here is that no cleartext error messages are returnwd; only // error-code-labels. So for a start we go for cleartext output. $exitCode = exec($cmd, $output, $retval); // Variable $exitCode is the last line of $output if no error occures, on // error it is numeric. Try to handle various error conditions and // provide useful error reporting. if (is_numeric($exitCode) === true && $exitCode > 0) { if (is_array($output) === true) { $msg = join('\\n', $output); } throw new RuntimeException("Failed invoking ZendCodeAnalyzer, exitcode was [{$exitCode}], retval was [{$retval}], output was [{$msg}]"); } if (is_array($output) === true) { foreach ($output as $finding) { // The first two lines of analyzer output contain // something like this: // > Zend Code Analyzer 1.2.2 // > Analyzing <filename>... // So skip these... $res = preg_match("/^.+\\(line ([0-9]+)\\):(.+)\$/", $finding, $regs); if (empty($regs) === true || $res === false) { continue; } $phpcsFile->addWarningOnLine(trim($regs[2]), $regs[1], '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(); $namespace = ''; $findTokens = array(T_CLASS, T_INTERFACE, T_NAMESPACE, T_CLOSE_TAG); $stackPtr = $phpcsFile->findNext($findTokens, $stackPtr + 1); while ($stackPtr !== false) { if ($tokens[$stackPtr]['code'] === T_CLOSE_TAG) { // We can stop here. The sniff will continue from the next open // tag when PHPCS reaches that token, if there is one. return; } // Keep track of what namespace we are in. if ($tokens[$stackPtr]['code'] === T_NAMESPACE) { $nsEnd = $phpcsFile->findNext(array(T_NS_SEPARATOR, T_STRING, T_WHITESPACE), $stackPtr + 1, null, true); $namespace = trim($phpcsFile->getTokensAsString($stackPtr + 1, $nsEnd - $stackPtr - 1)); $stackPtr = $nsEnd; } else { $nameToken = $phpcsFile->findNext(T_STRING, $stackPtr); $name = $tokens[$nameToken]['content']; if ($namespace !== '') { $name = $namespace . '\\' . $name; } $compareName = strtolower($name); if (isset($this->foundClasses[$compareName]) === true) { $type = strtolower($tokens[$stackPtr]['content']); $file = $this->foundClasses[$compareName]['file']; $line = $this->foundClasses[$compareName]['line']; $error = 'Duplicate %s name "%s" found; first defined in %s on line %s'; $data = array($type, $name, $file, $line); $phpcsFile->addWarning($error, $stackPtr, 'Found', $data); } else { $this->foundClasses[$compareName] = array('file' => $phpcsFile->getFilename(), 'line' => $tokens[$stackPtr]['line']); } } //end if $stackPtr = $phpcsFile->findNext($findTokens, $stackPtr + 1); } //end while }
/** * 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) { $lintPath = Config::getExecutablePath('gjslint'); if ($lintPath === null) { return; } $fileName = $phpcsFile->getFilename(); $cmd = "{$lintPath} --nosummary --notime --unix_mode \"{$fileName}\""; $msg = exec($cmd, $output, $retval); if (is_array($output) === false) { return; } foreach ($output as $finding) { $matches = array(); $numMatches = preg_match('/^(.*):([0-9]+):\\(.*?([0-9]+)\\)(.*)$/', $finding, $matches); if ($numMatches === 0) { continue; } // Skip error codes we are ignoring. $code = $matches[3]; if (in_array($code, $this->ignoreCodes) === true) { continue; } $line = (int) $matches[2]; $error = trim($matches[4]); $message = 'gjslint says: (%s) %s'; $data = array($code, $error); if (in_array($code, $this->errorCodes) === true) { $phpcsFile->addErrorOnLine($message, $line, 'ExternalToolError', $data); } else { $phpcsFile->addWarningOnLine($message, $line, 'ExternalTool', $data); } } //end foreach // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * Processes a token within the scope that this test is listening to. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * this token was found. * * @return void */ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr]['code'] === T_EXTENDS) { // Find the class name. $classNameToken = $phpcsFile->findNext(T_STRING, $stackPtr + 1); $className = $tokens[$classNameToken]['content']; } else { // Determine the name of the class that the static function // is being called on. But don't process class names represented by // variables as this can be an inexact science. $classNameToken = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); if ($tokens[$classNameToken]['code'] === T_VARIABLE) { return; } $className = $tokens[$classNameToken]['content']; } // Some systems are always available. if (isset($this->ignore[strtolower($className)]) === true) { return; } $includedClasses = array(); $fileName = strtolower($phpcsFile->getFilename()); $matches = array(); if (preg_match('|/systems/([^/]+)/([^/]+)?actions.inc$|', $fileName, $matches) !== 0) { // This is an actions file, which means we don't // have to include the system in which it exists // We know the system from the path. $includedClasses[$matches[1]] = true; } // Go searching for includeSystem, includeAsset or require/include // calls outside our scope. for ($i = 0; $i < $stackPtr; $i++) { // Skip classes and functions as will we never get // into their scopes when including this file, although // we have a chance of getting into IF's, WHILE's etc. if (($tokens[$i]['code'] === T_CLASS || $tokens[$i]['code'] === T_INTERFACE || $tokens[$i]['code'] === T_FUNCTION) && isset($tokens[$i]['scope_closer']) === true) { $i = $tokens[$i]['scope_closer']; continue; } $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i); if ($name !== false) { $includedClasses[$name] = true; // Special case for Widgets cause they are, well, special. } else { if (strtolower($tokens[$i]['content']) === 'includewidget') { $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $i + 1); $typeName = trim($tokens[$typeName]['content'], " '"); $includedClasses[strtolower($typeName) . 'widgettype'] = true; } } } //end for if (isset($includedClasses[strtolower($className)]) === false) { if ($tokens[$stackPtr]['code'] === T_EXTENDS) { $error = 'Class extends non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once'; $data = array($className); $phpcsFile->addError($error, $stackPtr, 'NotIncludedExtends', $data); } else { $error = 'Static method called on non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once'; $data = array($className); $phpcsFile->addError($error, $stackPtr, 'NotIncludedCall', $data); } } }
/** * Generate summary information to be used during report generation. * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file that has been processed. * * @return array */ public function prepareFileReport(File $phpcsFile) { $report = array('filename' => Common::stripBasepath($phpcsFile->getFilename(), $this->config->basepath), 'errors' => $phpcsFile->getErrorCount(), 'warnings' => $phpcsFile->getWarningCount(), 'fixable' => $phpcsFile->getFixableCount(), 'messages' => array()); if ($report['errors'] === 0 && $report['warnings'] === 0) { // Prefect score! return $report; } if ($this->config->recordErrors === false) { $message = 'Errors are not being recorded but this report requires error messages. '; $message .= 'This report will not show the correct information.'; $report['messages'][1][1] = array(array('message' => $message, 'source' => 'Internal.RecordErrors', 'severity' => 5, 'fixable' => false, 'type' => 'ERROR')); return $report; } $errors = array(); // Merge errors and warnings. foreach ($phpcsFile->getErrors() as $line => $lineErrors) { foreach ($lineErrors as $column => $colErrors) { $newErrors = array(); foreach ($colErrors as $data) { $newErrors[] = array('message' => $data['message'], 'source' => $data['source'], 'severity' => $data['severity'], 'fixable' => $data['fixable'], 'type' => 'ERROR'); } $errors[$line][$column] = $newErrors; } ksort($errors[$line]); } //end foreach foreach ($phpcsFile->getWarnings() as $line => $lineWarnings) { foreach ($lineWarnings as $column => $colWarnings) { $newWarnings = array(); foreach ($colWarnings as $data) { $newWarnings[] = array('message' => $data['message'], 'source' => $data['source'], 'severity' => $data['severity'], 'fixable' => $data['fixable'], 'type' => 'WARNING'); } if (isset($errors[$line]) === false) { $errors[$line] = array(); } if (isset($errors[$line][$column]) === true) { $errors[$line][$column] = array_merge($newWarnings, $errors[$line][$column]); } else { $errors[$line][$column] = $newWarnings; } } //end foreach ksort($errors[$line]); } //end foreach ksort($errors); $report['messages'] = $errors; return $report; }
/** * Processes the test. * * @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where the * token occurred. * @param int $stackPtr The position in the tokens stack * where the listening token type was * found. * * @return void * @see register() */ public final function process(File $phpcsFile, $stackPtr) { $file = $phpcsFile->getFilename(); if ($this->currFile !== $file) { // We have changed files, so clean up. $this->errorPos = array(); $this->currFile = $file; } $tokens = $phpcsFile->getTokens(); if (in_array($tokens[$stackPtr]['code'], $this->supplementaryTokens) === true) { $this->processSupplementary($phpcsFile, $stackPtr); } $type = $tokens[$stackPtr]['code']; // If the type is not set, then it must have been a token registered // with registerSupplementary(). if (isset($this->parsedPatterns[$type]) === false) { return; } $allErrors = array(); // Loop over each pattern that is listening to the current token type // that we are processing. foreach ($this->parsedPatterns[$type] as $patternInfo) { // If processPattern returns false, then the pattern that we are // checking the code with must not be designed to check that code. $errors = $this->processPattern($patternInfo, $phpcsFile, $stackPtr); if ($errors === false) { // The pattern didn't match. continue; } else { if (empty($errors) === true) { // The pattern matched, but there were no errors. break; } } foreach ($errors as $stackPtr => $error) { if (isset($this->errorPos[$stackPtr]) === false) { $this->errorPos[$stackPtr] = true; $allErrors[$stackPtr] = $error; } } } foreach ($allErrors as $stackPtr => $error) { $phpcsFile->addError($error, $stackPtr, 'Found'); } }