Example #1
0
 /**
  * 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;
 }
Example #2
0
 /**
  * Expands a ruleset reference into a list of sniff files.
  *
  * @param string $ref        The reference from the ruleset XML file.
  * @param string $rulesetDir The directory of the ruleset XML file, used to
  *                           evaluate relative paths.
  * @param int    $depth      How many nested processing steps we are in. This
  *                           is only used for debug output.
  *
  * @return array
  * @throws RuntimeException If the reference is invalid.
  */
 private function expandRulesetReference($ref, $rulesetDir, $depth = 0)
 {
     // Ignore internal sniffs codes as they are used to only
     // hide and change internal messages.
     if (substr($ref, 0, 9) === 'Internal.') {
         if (PHP_CODESNIFFER_VERBOSITY > 1) {
             echo str_repeat("\t", $depth);
             echo "\t\t* ignoring internal sniff code *" . PHP_EOL;
         }
         return array();
     }
     // As sniffs can't begin with a full stop, assume references in
     // this format are relative paths and attempt to convert them
     // to absolute paths. If this fails, let the reference run through
     // the normal checks and have it fail as normal.
     if (substr($ref, 0, 1) === '.') {
         $realpath = Util\Common::realpath($rulesetDir . '/' . $ref);
         if ($realpath !== false) {
             $ref = $realpath;
             if (PHP_CODESNIFFER_VERBOSITY > 1) {
                 echo str_repeat("\t", $depth);
                 echo "\t\t=> " . Util\Common::stripBasepath($ref, $this->config->basepath) . PHP_EOL;
             }
         }
     }
     // As sniffs can't begin with a tilde, assume references in
     // this format are relative to the user's home directory.
     if (substr($ref, 0, 2) === '~/') {
         $realpath = Util\Common::realpath($ref);
         if ($realpath !== false) {
             $ref = $realpath;
             if (PHP_CODESNIFFER_VERBOSITY > 1) {
                 echo str_repeat("\t", $depth);
                 echo "\t\t=> " . Util\Common::stripBasepath($ref, $this->config->basepath) . PHP_EOL;
             }
         }
     }
     if (is_file($ref) === true) {
         if (substr($ref, -9) === 'Sniff.php') {
             // A single external sniff.
             $this->rulesetDirs[] = dirname(dirname(dirname($ref)));
             return array($ref);
         }
     } else {
         // See if this is a whole standard being referenced.
         $path = Util\Standards::getInstalledStandardPath($ref);
         if (Util\Common::isPharFile($path) === true && strpos($path, 'ruleset.xml') === false) {
             // If the ruleset exists inside the phar file, use it.
             if (file_exists($path . DIRECTORY_SEPARATOR . 'ruleset.xml') === true) {
                 $path = $path . DIRECTORY_SEPARATOR . 'ruleset.xml';
             } else {
                 $path = null;
             }
         }
         if ($path !== null) {
             $ref = $path;
             if (PHP_CODESNIFFER_VERBOSITY > 1) {
                 echo str_repeat("\t", $depth);
                 echo "\t\t=> " . Util\Common::stripBasepath($ref, $this->config->basepath) . PHP_EOL;
             }
         } else {
             if (is_dir($ref) === false) {
                 // Work out the sniff path.
                 $sepPos = strpos($ref, DIRECTORY_SEPARATOR);
                 if ($sepPos !== false) {
                     $stdName = substr($ref, 0, $sepPos);
                     $path = substr($ref, $sepPos);
                 } else {
                     $parts = explode('.', $ref);
                     $stdName = $parts[0];
                     if (count($parts) === 1) {
                         // A whole standard?
                         $path = '';
                     } else {
                         if (count($parts) === 2) {
                             // A directory of sniffs?
                             $path = DIRECTORY_SEPARATOR . 'Sniffs' . DIRECTORY_SEPARATOR . $parts[1];
                         } else {
                             // A single sniff?
                             $path = DIRECTORY_SEPARATOR . 'Sniffs' . DIRECTORY_SEPARATOR . $parts[1] . DIRECTORY_SEPARATOR . $parts[2] . 'Sniff.php';
                         }
                     }
                 }
                 $newRef = false;
                 $stdPath = Util\Standards::getInstalledStandardPath($stdName);
                 if ($stdPath !== null && $path !== '') {
                     if (Util\Common::isPharFile($stdPath) === true && strpos($stdPath, 'ruleset.xml') === false) {
                         // Phar files can only return the directory,
                         // since ruleset can be omitted if building one standard.
                         $newRef = Util\Common::realpath($stdPath . $path);
                     } else {
                         $newRef = Util\Common::realpath(dirname($stdPath) . $path);
                     }
                 }
                 if ($newRef === false) {
                     // The sniff is not locally installed, so check if it is being
                     // referenced as a remote sniff outside the install. We do this
                     // by looking through all directories where we have found ruleset
                     // files before, looking for ones for this particular standard,
                     // and seeing if it is in there.
                     foreach ($this->rulesetDirs as $dir) {
                         if (strtolower(basename($dir)) !== strtolower($stdName)) {
                             continue;
                         }
                         $newRef = Util\Common::realpath($dir . $path);
                         if ($newRef !== false) {
                             $ref = $newRef;
                         }
                     }
                 } else {
                     $ref = $newRef;
                 }
                 if (PHP_CODESNIFFER_VERBOSITY > 1) {
                     echo str_repeat("\t", $depth);
                     echo "\t\t=> " . Util\Common::stripBasepath($ref, $this->config->basepath) . PHP_EOL;
                 }
             }
         }
         //end if
     }
     //end if
     if (is_dir($ref) === true) {
         if (is_file($ref . DIRECTORY_SEPARATOR . 'ruleset.xml') === true) {
             // We are referencing an external coding standard.
             if (PHP_CODESNIFFER_VERBOSITY > 1) {
                 echo str_repeat("\t", $depth);
                 echo "\t\t* rule is referencing a standard using directory name; processing *" . PHP_EOL;
             }
             return $this->processRuleset($ref . DIRECTORY_SEPARATOR . 'ruleset.xml', $depth + 2);
         } else {
             // We are referencing a whole directory of sniffs.
             if (PHP_CODESNIFFER_VERBOSITY > 1) {
                 echo str_repeat("\t", $depth);
                 echo "\t\t* rule is referencing a directory of sniffs *" . PHP_EOL;
                 echo str_repeat("\t", $depth);
                 echo "\t\tAdding sniff files from directory" . PHP_EOL;
             }
             return $this->expandSniffDirectory($ref, $depth + 1);
         }
     } else {
         if (is_file($ref) === false) {
             $error = "Referenced sniff \"{$ref}\" does not exist";
             throw new RuntimeException($error);
         }
         if (substr($ref, -9) === 'Sniff.php') {
             // A single sniff.
             return array($ref);
         } else {
             // Assume an external ruleset.xml file.
             if (PHP_CODESNIFFER_VERBOSITY > 1) {
                 echo str_repeat("\t", $depth);
                 echo "\t\t* rule is referencing a standard using ruleset path; processing *" . PHP_EOL;
             }
             return $this->processRuleset($ref, $depth + 2);
         }
     }
     //end if
 }
Example #3
0
 /**
  * Performs the run.
  *
  * @return int The number of errors and warnings found.
  */
 private function run()
 {
     // The class that manages all reporters for the run.
     $this->reporter = new Reporter($this->config);
     // Include bootstrap files.
     foreach ($this->config->bootstrap as $bootstrap) {
         include $bootstrap;
     }
     if ($this->config->stdin === true) {
         $fileContents = $this->config->stdinContent;
         if ($fileContents === null) {
             $handle = fopen('php://stdin', 'r');
             stream_set_blocking($handle, true);
             $fileContents = stream_get_contents($handle);
             fclose($handle);
         }
         $todo = new FileList($this->config, $this->ruleset);
         $dummy = new DummyFile($fileContents, $this->ruleset, $this->config);
         $todo->addFile($dummy->path, $dummy);
         $numFiles = 1;
     } else {
         if (empty($this->config->files) === true) {
             echo 'ERROR: You must supply at least one file or directory to process.' . PHP_EOL . PHP_EOL;
             $this->config->printUsage();
             exit(0);
         }
         if (PHP_CODESNIFFER_VERBOSITY > 0) {
             echo 'Creating file list... ';
         }
         $todo = new FileList($this->config, $this->ruleset);
         $numFiles = count($todo);
         if (PHP_CODESNIFFER_VERBOSITY > 0) {
             echo "DONE ({$numFiles} files in queue)" . PHP_EOL;
         }
         if ($this->config->cache === true) {
             if (PHP_CODESNIFFER_VERBOSITY > 0) {
                 echo 'Loading cache... ';
             }
             Cache::load($this->ruleset, $this->config);
             if (PHP_CODESNIFFER_VERBOSITY > 0) {
                 $size = Cache::getSize();
                 echo "DONE ({$size} files in cache)" . PHP_EOL;
             }
         }
     }
     //end if
     // Turn all sniff errors into exceptions.
     set_error_handler(array($this, 'handleErrors'));
     // If verbosity is too high, turn off parallelism so the
     // debug output is clean.
     if (PHP_CODESNIFFER_VERBOSITY > 1) {
         $this->config->parallel = 1;
     }
     // If the PCNTL extension isn't installed, we can't fork.
     if (function_exists('pcntl_fork') === false) {
         $this->config->parallel = 1;
     }
     $lastDir = '';
     if ($this->config->parallel === 1) {
         // Running normally.
         $numProcessed = 0;
         foreach ($todo as $path => $file) {
             $currDir = dirname($path);
             if ($lastDir !== $currDir) {
                 if (PHP_CODESNIFFER_VERBOSITY > 0) {
                     echo 'Changing into directory ' . Common::stripBasepath($currDir, $this->config->basepath) . PHP_EOL;
                 }
                 $lastDir = $currDir;
             }
             $this->processFile($file);
             $numProcessed++;
             $this->printProgress($file, $numFiles, $numProcessed);
         }
     } else {
         // Batching and forking.
         $childProcs = array();
         $numFiles = count($todo);
         $numPerBatch = ceil($numFiles / $this->config->parallel);
         for ($batch = 0; $batch < $this->config->parallel; $batch++) {
             $startAt = $batch * $numPerBatch;
             if ($startAt >= $numFiles) {
                 break;
             }
             $endAt = $startAt + $numPerBatch;
             if ($endAt > $numFiles) {
                 $endAt = $numFiles;
             }
             $childOutFilename = tempnam(sys_get_temp_dir(), 'phpcs-child');
             $pid = pcntl_fork();
             if ($pid === -1) {
                 throw new RuntimeException('Failed to create child process');
             } else {
                 if ($pid !== 0) {
                     $childProcs[] = array('pid' => $pid, 'out' => $childOutFilename);
                 } else {
                     // Move forward to the start of the batch.
                     $todo->rewind();
                     for ($i = 0; $i < $startAt; $i++) {
                         $todo->next();
                     }
                     // Reset the reporter to make sure only figures from this
                     // file batch are recorded.
                     $this->reporter->totalFiles = 0;
                     $this->reporter->totalErrors = 0;
                     $this->reporter->totalWarnings = 0;
                     $this->reporter->totalFixable = 0;
                     // Process the files.
                     $pathsProcessed = array();
                     ob_start();
                     for ($i = $startAt; $i < $endAt; $i++) {
                         $path = $todo->key();
                         $file = $todo->current();
                         $currDir = dirname($path);
                         if ($lastDir !== $currDir) {
                             if (PHP_CODESNIFFER_VERBOSITY > 0) {
                                 echo 'Changing into directory ' . Common::stripBasepath($currDir, $this->config->basepath) . PHP_EOL;
                             }
                             $lastDir = $currDir;
                         }
                         $this->processFile($file);
                         $pathsProcessed[] = $path;
                         $todo->next();
                     }
                     $debugOutput = ob_get_contents();
                     ob_end_clean();
                     // Write information about the run to the filesystem
                     // so it can be picked up by the main process.
                     $childOutput = array('totalFiles' => $this->reporter->totalFiles, 'totalErrors' => $this->reporter->totalErrors, 'totalWarnings' => $this->reporter->totalWarnings, 'totalFixable' => $this->reporter->totalFixable, 'totalFixed' => $this->reporter->totalFixed);
                     $output = '<' . '?php' . "\n" . ' $childOutput = ';
                     $output .= var_export($childOutput, true);
                     $output .= ";\n\$debugOutput = ";
                     $output .= var_export($debugOutput, true);
                     if ($this->config->cache === true) {
                         $childCache = array();
                         foreach ($pathsProcessed as $path) {
                             $childCache[$path] = Cache::get($path);
                         }
                         $output .= ";\n\$childCache = ";
                         $output .= var_export($childCache, true);
                     }
                     $output .= ";\n?" . '>';
                     file_put_contents($childOutFilename, $output);
                     exit($pid);
                 }
             }
             //end if
         }
         //end for
         $this->processChildProcs($childProcs);
     }
     //end if
     restore_error_handler();
     if (PHP_CODESNIFFER_VERBOSITY === 0 && $this->config->interactive === false && $this->config->showProgress === true) {
         echo PHP_EOL . PHP_EOL;
     }
     if ($this->config->cache === true) {
         Cache::save();
     }
     $ignoreWarnings = Config::getConfigData('ignore_warnings_on_exit');
     $ignoreErrors = Config::getConfigData('ignore_errors_on_exit');
     $return = $this->reporter->totalErrors + $this->reporter->totalWarnings;
     if ($ignoreErrors !== null) {
         $ignoreErrors = (bool) $ignoreErrors;
         if ($ignoreErrors === true) {
             $return -= $this->reporter->totalErrors;
         }
     }
     if ($ignoreWarnings !== null) {
         $ignoreWarnings = (bool) $ignoreWarnings;
         if ($ignoreWarnings === true) {
             $return -= $this->reporter->totalWarnings;
         }
     }
     return $return;
 }