Provides helper functions that act upon a token array and modify the file content.
Author: Greg Sherwood (gsherwood@squiz.net)
Example #1
0
 /**
  * Starts the stack traversal and tells listeners when tokens are found.
  *
  * @param string $contents The contents to parse. If NULL, the content
  *                         is taken from the file system.
  *
  * @return void
  */
 public function start($contents = null)
 {
     $this->_errors = array();
     $this->_warnings = array();
     $this->_errorCount = 0;
     $this->_warningCount = 0;
     $this->_fixableCount = 0;
     $this->_parse($contents);
     $this->fixer->startFile($this);
     if (PHP_CODESNIFFER_VERBOSITY > 2) {
         echo "\t*** START TOKEN PROCESSING ***" . PHP_EOL;
     }
     $foundCode = false;
     $ignoring = false;
     // Foreach of the listeners that have registered to listen for this
     // token, get them to process it.
     foreach ($this->_tokens as $stackPtr => $token) {
         // Check for ignored lines.
         if ($token['code'] === T_COMMENT || $token['code'] === T_DOC_COMMENT) {
             if (strpos($token['content'], '@codingStandardsIgnoreStart') !== false) {
                 $ignoring = true;
             } else {
                 if (strpos($token['content'], '@codingStandardsIgnoreEnd') !== false) {
                     $ignoring = false;
                     // Ignore this comment too.
                     $this->_ignoredLines[$token['line']] = true;
                 } else {
                     if (strpos($token['content'], '@codingStandardsIgnoreFile') !== false) {
                         // Ignoring the whole file, just a little late.
                         $this->_errors = array();
                         $this->_warnings = array();
                         $this->_errorCount = 0;
                         $this->_warningCount = 0;
                         $this->_fixableCount = 0;
                         return;
                     } else {
                         if (strpos($token['content'], '@codingStandardsChangeSetting') !== false) {
                             $start = strpos($token['content'], '@codingStandardsChangeSetting');
                             $comment = substr($token['content'], $start + 30);
                             $parts = explode(' ', $comment);
                             $sniffParts = explode('.', $parts[0]);
                             $listenerClass = $sniffParts[0] . '_Sniffs_' . $sniffParts[1] . '_' . $sniffParts[2] . 'Sniff';
                             $this->phpcs->setSniffProperty($listenerClass, $parts[1], $parts[2]);
                         }
                     }
                 }
             }
             //end if
         }
         //end if
         if ($ignoring === true) {
             $this->_ignoredLines[$token['line']] = true;
             continue;
         }
         if (PHP_CODESNIFFER_VERBOSITY > 2) {
             $type = $token['type'];
             $content = str_replace($this->eolChar, '\\n', $token['content']);
             $content = str_replace(' ', "·", $content);
             echo "\t\tProcess token {$stackPtr}: {$type} => {$content}" . PHP_EOL;
         }
         $tokenType = $token['code'];
         if ($tokenType !== T_INLINE_HTML) {
             $foundCode = true;
         }
         if (isset($this->_listeners[$tokenType]) === false) {
             continue;
         }
         foreach ($this->_listeners[$tokenType] as $listenerData) {
             // Make sure this sniff supports the tokenizer
             // we are currently using.
             $listener = $listenerData['listener'];
             $class = $listenerData['class'];
             if (in_array($this->tokenizerType, $listenerData['tokenizers']) === false) {
                 continue;
             }
             // If the file path matches one of our ignore patterns, skip it.
             $parts = explode('_', str_replace('\\', '_', $class));
             if (isset($parts[3]) === true) {
                 $source = $parts[0] . '.' . $parts[2] . '.' . substr($parts[3], 0, -5);
                 $patterns = $this->phpcs->getIgnorePatterns($source);
                 foreach ($patterns as $pattern => $type) {
                     // While there is support for a type of each pattern
                     // (absolute or relative) we don't actually support it here.
                     $replacements = array('\\,' => ',', '*' => '.*');
                     $pattern = strtr($pattern, $replacements);
                     if (preg_match("|{$pattern}|i", $this->_file) === 1) {
                         continue 2;
                     }
                 }
             }
             $this->setActiveListener($class);
             if (PHP_CODESNIFFER_VERBOSITY > 2) {
                 $startTime = microtime(true);
                 echo "\t\t\tProcessing " . $this->_activeListener . '... ';
             }
             $listener->process($this, $stackPtr);
             if (PHP_CODESNIFFER_VERBOSITY > 2) {
                 $timeTaken = microtime(true) - $startTime;
                 if (isset($this->_listenerTimes[$this->_activeListener]) === false) {
                     $this->_listenerTimes[$this->_activeListener] = 0;
                 }
                 $this->_listenerTimes[$this->_activeListener] += $timeTaken;
                 $timeTaken = round($timeTaken, 4);
                 echo "DONE in {$timeTaken} seconds" . PHP_EOL;
             }
             $this->_activeListener = '';
         }
         //end foreach
     }
     //end foreach
     // Remove errors and warnings for ignored lines.
     foreach ($this->_ignoredLines as $line => $ignore) {
         if (isset($this->_errors[$line]) === true) {
             if ($this->_recordErrors === false) {
                 $this->_errorCount -= $this->_errors[$line];
             } else {
                 foreach ($this->_errors[$line] as $col => $errors) {
                     $this->_errorCount -= count($errors);
                 }
             }
             unset($this->_errors[$line]);
         }
         if (isset($this->_warnings[$line]) === true) {
             if ($this->_recordErrors === false) {
                 $this->_errorCount -= $this->_warnings[$line];
             } else {
                 foreach ($this->_warnings[$line] as $col => $warnings) {
                     $this->_warningCount -= count($warnings);
                 }
             }
             unset($this->_warnings[$line]);
         }
     }
     //end foreach
     if ($this->_recordErrors === false) {
         $this->_errors = array();
         $this->_warnings = array();
     }
     // If short open tags are off but the file being checked uses
     // short open tags, the whole content will be inline HTML
     // and nothing will be checked. So try and handle this case.
     if ($foundCode === false) {
         $shortTags = (bool) ini_get('short_open_tag');
         if ($shortTags === false) {
             $error = 'No PHP code was found in this file and short open tags are not allowed by this install of PHP. This file may be using short open tags but PHP does not allow them.';
             $this->addWarning($error, null, 'Internal.NoCodeFound');
         }
     }
     if (PHP_CODESNIFFER_VERBOSITY > 2) {
         echo "\t*** END TOKEN PROCESSING ***" . PHP_EOL;
     }
     if (PHP_CODESNIFFER_VERBOSITY > 2) {
         echo "\t*** START SNIFF PROCESSING REPORT ***" . PHP_EOL;
         asort($this->_listenerTimes, SORT_NUMERIC);
         $this->_listenerTimes = array_reverse($this->_listenerTimes, true);
         foreach ($this->_listenerTimes as $listener => $timeTaken) {
             echo "\t{$listener}: " . round($timeTaken, 4) . ' secs' . PHP_EOL;
         }
         echo "\t*** END SNIFF PROCESSING REPORT ***" . PHP_EOL;
     }
 }
Example #2
0
 /**
  * Starts the stack traversal and tells listeners when tokens are found.
  *
  * @param string $contents The contents to parse. If NULL, the content
  *                         is taken from the file system.
  *
  * @return void
  */
 public function start($contents = null)
 {
     $this->_errors = array();
     $this->_warnings = array();
     $this->_errorCount = 0;
     $this->_warningCount = 0;
     $this->_fixableCount = 0;
     // Reset the ignored lines because lines numbers may have changed
     // if we are fixing this file.
     self::$_ignoredLines = array();
     try {
         $this->eolChar = self::detectLineEndings($this->_file, $contents);
     } catch (PHP_CodeSniffer_Exception $e) {
         $this->addWarning($e->getMessage(), null, 'Internal.DetectLineEndings');
         return;
     }
     // If this is standard input, see if a filename was passed in as well.
     // This is done by including: phpcs_input_file: [file path]
     // as the first line of content.
     if ($this->_file === 'STDIN' && $contents !== null) {
         if (substr($contents, 0, 17) === 'phpcs_input_file:') {
             $eolPos = strpos($contents, $this->eolChar);
             $filename = trim(substr($contents, 17, $eolPos - 17));
             $contents = substr($contents, $eolPos + strlen($this->eolChar));
             $this->_file = $filename;
         }
     }
     $this->_parse($contents);
     $this->fixer->startFile($this);
     if (PHP_CODESNIFFER_VERBOSITY > 2) {
         echo "\t*** START TOKEN PROCESSING ***" . PHP_EOL;
     }
     $foundCode = false;
     $listeners = $this->phpcs->getSniffs();
     $listenerIgnoreTo = array();
     $inTests = defined('PHP_CODESNIFFER_IN_TESTS');
     // Foreach of the listeners that have registered to listen for this
     // token, get them to process it.
     foreach ($this->_tokens as $stackPtr => $token) {
         // Check for ignored lines.
         if ($token['code'] === T_COMMENT || $token['code'] === T_DOC_COMMENT_TAG || $inTests === true && $token['code'] === T_INLINE_HTML) {
             if (strpos($token['content'], '@codingStandards') !== false) {
                 if (strpos($token['content'], '@codingStandardsIgnoreFile') !== false) {
                     // Ignoring the whole file, just a little late.
                     $this->_errors = array();
                     $this->_warnings = array();
                     $this->_errorCount = 0;
                     $this->_warningCount = 0;
                     $this->_fixableCount = 0;
                     return;
                 } else {
                     if (strpos($token['content'], '@codingStandardsChangeSetting') !== false) {
                         $start = strpos($token['content'], '@codingStandardsChangeSetting');
                         $comment = substr($token['content'], $start + 30);
                         $parts = explode(' ', $comment);
                         $sniffParts = explode('.', $parts[0]);
                         $listenerClass = $sniffParts[0] . '_Sniffs_' . $sniffParts[1] . '_' . $sniffParts[2] . 'Sniff';
                         $this->phpcs->setSniffProperty($listenerClass, $parts[1], $parts[2]);
                     }
                 }
                 //end if
             }
             //end if
         }
         //end if
         if (PHP_CODESNIFFER_VERBOSITY > 2) {
             $type = $token['type'];
             $content = PHP_CodeSniffer::prepareForOutput($token['content']);
             echo "\t\tProcess token {$stackPtr}: {$type} => {$content}" . PHP_EOL;
         }
         if ($token['code'] !== T_INLINE_HTML) {
             $foundCode = true;
         }
         if (isset($this->_listeners[$token['code']]) === false) {
             continue;
         }
         foreach ($this->_listeners[$token['code']] as $listenerData) {
             if (isset($this->_ignoredListeners[$listenerData['class']]) === true || isset($listenerIgnoreTo[$listenerData['class']]) === true && $listenerIgnoreTo[$listenerData['class']] > $stackPtr) {
                 // This sniff is ignoring past this token, or the whole file.
                 continue;
             }
             // Make sure this sniff supports the tokenizer
             // we are currently using.
             $class = $listenerData['class'];
             if (isset($listenerData['tokenizers'][$this->tokenizerType]) === false) {
                 continue;
             }
             // If the file path matches one of our ignore patterns, skip it.
             // While there is support for a type of each pattern
             // (absolute or relative) we don't actually support it here.
             foreach ($listenerData['ignore'] as $pattern) {
                 // We assume a / directory separator, as do the exclude rules
                 // most developers write, so we need a special case for any system
                 // that is different.
                 if (DIRECTORY_SEPARATOR === '\\') {
                     $pattern = str_replace('/', '\\\\', $pattern);
                 }
                 $pattern = '`' . $pattern . '`i';
                 if (preg_match($pattern, $this->_file) === 1) {
                     $this->_ignoredListeners[$class] = true;
                     continue 2;
                 }
             }
             $this->_activeListener = $class;
             if (PHP_CODESNIFFER_VERBOSITY > 2) {
                 $startTime = microtime(true);
                 echo "\t\t\tProcessing " . $this->_activeListener . '... ';
             }
             $ignoreTo = $listeners[$class]->process($this, $stackPtr);
             if ($ignoreTo !== null) {
                 $listenerIgnoreTo[$this->_activeListener] = $ignoreTo;
             }
             if (PHP_CODESNIFFER_VERBOSITY > 2) {
                 $timeTaken = microtime(true) - $startTime;
                 if (isset($this->_listenerTimes[$this->_activeListener]) === false) {
                     $this->_listenerTimes[$this->_activeListener] = 0;
                 }
                 $this->_listenerTimes[$this->_activeListener] += $timeTaken;
                 $timeTaken = round($timeTaken, 4);
                 echo "DONE in {$timeTaken} seconds" . PHP_EOL;
             }
             $this->_activeListener = '';
         }
         //end foreach
     }
     //end foreach
     if ($this->_recordErrors === false) {
         $this->_errors = array();
         $this->_warnings = array();
     }
     // If short open tags are off but the file being checked uses
     // short open tags, the whole content will be inline HTML
     // and nothing will be checked. So try and handle this case.
     if ($foundCode === false && $this->tokenizerType === 'PHP') {
         $shortTags = (bool) ini_get('short_open_tag');
         if ($shortTags === false) {
             $error = 'No PHP code was found in this file and short open tags are not allowed by this install of PHP. This file may be using short open tags but PHP does not allow them.';
             $this->addWarning($error, null, 'Internal.NoCodeFound');
         }
     }
     if (PHP_CODESNIFFER_VERBOSITY > 2) {
         echo "\t*** END TOKEN PROCESSING ***" . PHP_EOL;
         echo "\t*** START SNIFF PROCESSING REPORT ***" . PHP_EOL;
         asort($this->_listenerTimes, SORT_NUMERIC);
         $this->_listenerTimes = array_reverse($this->_listenerTimes, true);
         foreach ($this->_listenerTimes as $listener => $timeTaken) {
             echo "\t{$listener}: " . round($timeTaken, 4) . ' secs' . PHP_EOL;
         }
         echo "\t*** END SNIFF PROCESSING REPORT ***" . PHP_EOL;
     }
 }