PHP_CodeSniffer
[ class tree: PHP_CodeSniffer ] [ index: PHP_CodeSniffer ] [ all elements ]

Source for file Tokenizer.php

Documentation is available at Tokenizer.php

  1. <?php
  2. /**
  3.  * The base tokenizer class.
  4.  *
  5.  * @author    Greg Sherwood <gsherwood@squiz.net>
  6.  * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
  7.  * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
  8.  */
  9.  
  10. namespace PHP_CodeSniffer\Tokenizers;
  11.  
  12. use PHP_CodeSniffer\Exceptions\RuntimeException;
  13. use PHP_CodeSniffer\Util;
  14.  
  15. abstract class Tokenizer
  16. {
  17.  
  18.     /**
  19.      * The config data for the run.
  20.      *
  21.      * @var \PHP_CodeSniffer\Config 
  22.      */
  23.     protected $config = null;
  24.  
  25.     /**
  26.      * The EOL char used in the content.
  27.      *
  28.      * @var string 
  29.      */
  30.     protected $eolChar = array();
  31.  
  32.     /**
  33.      * A token-based representation of the content.
  34.      *
  35.      * @var array 
  36.      */
  37.     protected $tokens = array();
  38.  
  39.     /**
  40.      * Known lengths of tokens.
  41.      *
  42.      * @var array<int, int>
  43.      */
  44.     public $knownLengths = array();
  45.  
  46.     /**
  47.      * A list of lines being ignored due to error suppression comments.
  48.      *
  49.      * @var array 
  50.      */
  51.     public $ignoredLines = array();
  52.  
  53.  
  54.     /**
  55.      * Initialise and run the tokenizer.
  56.      *
  57.      * @param string                         $content The content to tokenize,
  58.      * @param \PHP_CodeSniffer\Config | null $config  The config data for the run.
  59.      * @param string                         $eolChar The EOL char used in the content.
  60.      *
  61.      * @return void 
  62.      * @throws TokenizerException If the file appears to be minified.
  63.      */
  64.     public function __construct($content$config$eolChar='\n')
  65.     {
  66.         $this->eolChar $eolChar;
  67.  
  68.         $this->config $config;
  69.         $this->tokens $this->tokenize($content);
  70.  
  71.         if ($config === null{
  72.             return;
  73.         }
  74.  
  75.         $this->createPositionMap();
  76.         $this->createTokenMap();
  77.         $this->createParenthesisNestingMap();
  78.         $this->createScopeMap();
  79.         $this->createLevelMap();
  80.  
  81.         // Allow the tokenizer to do additional processing if required.
  82.         $this->processAdditional();
  83.  
  84.     }//end __construct()
  85.  
  86.  
  87.     /**
  88.      * Checks the content to see if it looks minified.
  89.      *
  90.      * @param string $content The content to tokenize,
  91.      * @param string $eolChar The EOL char used in the content.
  92.      *
  93.      * @return boolean 
  94.      */
  95.     protected function isMinifiedContent($content$eolChar='\n')
  96.     {
  97.         // Minified files often have a very large number of characters per line
  98.         // and cause issues when tokenizing.
  99.         $numChars strlen($content);
  100.         $numLines (substr_count($content$eolChar+ 1);
  101.         $average  ($numChars $numLines);
  102.         if ($average > 100{
  103.             return true;
  104.         }
  105.  
  106.         return false;
  107.  
  108.     }//end isMinifiedContent()
  109.  
  110.  
  111.     /**
  112.      * Gets the array of tokens.
  113.      *
  114.      * @return array 
  115.      */
  116.     public function getTokens()
  117.     {
  118.         return $this->tokens;
  119.  
  120.     }//end getTokens()
  121.  
  122.  
  123.     /**
  124.      * Creates an array of tokens when given some content.
  125.      *
  126.      * @param string $string The string to tokenize.
  127.      *
  128.      * @return array 
  129.      */
  130.     abstract protected function tokenize($string);
  131.  
  132.  
  133.     /**
  134.      * Performs additional processing after main tokenizing.
  135.      *
  136.      * @return void 
  137.      */
  138.     abstract protected function processAdditional();
  139.  
  140.  
  141.     /**
  142.      * Sets token position information.
  143.      *
  144.      * Can also convert tabs into spaces. Each tab can represent between
  145.      * 1 and $width spaces, so this cannot be a straight string replace.
  146.      *
  147.      * @return void 
  148.      */
  149.     private function createPositionMap()
  150.     {
  151.         $currColumn = 1;
  152.         $lineNumber = 1;
  153.         $eolLen     (strlen($this->eolChar* -1);
  154.         $ignoring   = false;
  155.         $inTests    defined('PHP_CODESNIFFER_IN_TESTS');
  156.  
  157.         $checkEncoding = false;
  158.         if (function_exists('iconv_strlen'=== true{
  159.             $checkEncoding = true;
  160.         }
  161.  
  162.         $checkAnnotations $this->config->annotations;
  163.  
  164.         $this->tokensWithTabs = array(
  165.                                  T_WHITESPACE               => true,
  166.                                  T_COMMENT                  => true,
  167.                                  T_DOC_COMMENT              => true,
  168.                                  T_DOC_COMMENT_WHITESPACE   => true,
  169.                                  T_DOC_COMMENT_STRING       => true,
  170.                                  T_CONSTANT_ENCAPSED_STRING => true,
  171.                                  T_DOUBLE_QUOTED_STRING     => true,
  172.                                  T_HEREDOC                  => true,
  173.                                  T_NOWDOC                   => true,
  174.                                  T_INLINE_HTML              => true,
  175.                                 );
  176.  
  177.         $this->numTokens count($this->tokens);
  178.         for ($i = 0; $i $this->numTokens$i++{
  179.             $this->tokens[$i]['line']   $lineNumber;
  180.             $this->tokens[$i]['column'$currColumn;
  181.  
  182.             if (isset($this->knownLengths[$this->tokens[$i]['code']]=== true{
  183.                 // There are no tabs in the tokens we know the length of.
  184.                 $length      $this->knownLengths[$this->tokens[$i]['code']];
  185.                 $currColumn += $length;
  186.             else if ($this->config->tabWidth === 0
  187.                 || isset($this->tokensWithTabs[$this->tokens[$i]['code']]=== false
  188.                 || strpos($this->tokens[$i]['content']"\t"=== false
  189.             {
  190.                 // There are no tabs in this content, or we aren't replacing them.
  191.                 if ($checkEncoding === true{
  192.                     // Not using the default encoding, so take a bit more care.
  193.                     $length @iconv_strlen($this->tokens[$i]['content']$this->config->encoding);
  194.                     if ($length === false{
  195.                         // String contained invalid characters, so revert to default.
  196.                         $length strlen($this->tokens[$i]['content']);
  197.                     }
  198.                 else {
  199.                     $length strlen($this->tokens[$i]['content']);
  200.                 }
  201.  
  202.                 $currColumn += $length;
  203.             else {
  204.                 $this->replaceTabsInToken($this->tokens[$i]);
  205.                 $length      $this->tokens[$i]['length'];
  206.                 $currColumn += $length;
  207.             }//end if
  208.  
  209.             $this->tokens[$i]['length'$length;
  210.  
  211.             if (isset($this->knownLengths[$this->tokens[$i]['code']]=== false
  212.                 && strpos($this->tokens[$i]['content']$this->eolChar!== false
  213.             {
  214.                 $lineNumber++;
  215.                 $currColumn = 1;
  216.  
  217.                 // Newline chars are not counted in the token length.
  218.                 $this->tokens[$i]['length'+= $eolLen;
  219.             }
  220.  
  221.             if ($checkAnnotations === true
  222.                 && ($this->tokens[$i]['code'=== T_COMMENT
  223.                 || $this->tokens[$i]['code'=== T_DOC_COMMENT_TAG
  224.                 || ($inTests === true && $this->tokens[$i]['code'=== T_INLINE_HTML))
  225.             {
  226.                 if (strpos($this->tokens[$i]['content']'@codingStandards'!== false{
  227.                     if ($ignoring === false
  228.                         && strpos($this->tokens[$i]['content']'@codingStandardsIgnoreStart'!== false
  229.                     {
  230.                         $ignoring = true;
  231.                     else if ($ignoring === true
  232.                         && strpos($this->tokens[$i]['content']'@codingStandardsIgnoreEnd'!== false
  233.                     {
  234.                         $ignoring = false;
  235.                         // Ignore this comment too.
  236.                         $this->ignoredLines[$this->tokens[$i]['line']] = true;
  237.                     else if ($ignoring === false
  238.                         && strpos($this->tokens[$i]['content']'@codingStandardsIgnoreLine'!== false
  239.                     {
  240.                         $this->ignoredLines[($this->tokens[$i]['line'+ 1)= true;
  241.                         // Ignore this comment too.
  242.                         $this->ignoredLines[$this->tokens[$i]['line']] = true;
  243.                     }
  244.                 }
  245.             }//end if
  246.  
  247.             if ($ignoring === true{
  248.                 $this->ignoredLines[$this->tokens[$i]['line']] = true;
  249.             }
  250.         }//end for
  251.  
  252.     }//end createPositionMap()
  253.  
  254.  
  255.     /**
  256.      * Replaces tabs in original token content with spaces.
  257.      *
  258.      * Each tab can represent between 1 and $config->tabWidth spaces,
  259.      * so this cannot be a straight string replace. The original content
  260.      * is placed into an orig_content index and the new token length is also
  261.      * set in the length index.
  262.      *
  263.      * @param array  $token   The token to replace tabs inside.
  264.      * @param string $prefix  The character to use to represent the start of a tab.
  265.      * @param string $padding The character to use to represent the end of a tab.
  266.      *
  267.      * @return void 
  268.      */
  269.     public function replaceTabsInToken(&$token$prefix=' '$padding=' ')
  270.     {
  271.         $checkEncoding = false;
  272.         if (function_exists('iconv_strlen'=== true{
  273.             $checkEncoding = true;
  274.         }
  275.  
  276.         $currColumn $token['column'];
  277.         $tabWidth   $this->config->tabWidth;
  278.         if ($tabWidth === 0{
  279.             $tabWidth = 1;
  280.         }
  281.  
  282.         if (str_replace("\t"''$token['content']=== ''{
  283.             // String only contains tabs, so we can shortcut the process.
  284.             $numTabs strlen($token['content']);
  285.  
  286.             $newContent   '';
  287.             $firstTabSize ($tabWidth (($currColumn - 1$tabWidth));
  288.             $length       ($firstTabSize ($tabWidth ($numTabs - 1)));
  289.             $currColumn  += $length;
  290.             $newContent   $prefix.str_repeat($padding($length - 1));
  291.         else {
  292.             // We need to determine the length of each tab.
  293.             $tabs explode("\t"$token['content']);
  294.  
  295.             $numTabs    (count($tabs- 1);
  296.             $tabNum     = 0;
  297.             $newContent '';
  298.             $length     = 0;
  299.  
  300.             foreach ($tabs as $content{
  301.                 if ($content !== ''{
  302.                     $newContent .= $content;
  303.                     if ($checkEncoding === true{
  304.                         // Not using the default encoding, so take a bit more care.
  305.                         $contentLength @iconv_strlen($content$this->config->encoding);
  306.                         if ($contentLength === false{
  307.                             // String contained invalid characters, so revert to default.
  308.                             $contentLength strlen($content);
  309.                         }
  310.                     else {
  311.                         $contentLength strlen($content);
  312.                     }
  313.  
  314.                     $currColumn += $contentLength;
  315.                     $length     += $contentLength;
  316.                 }
  317.  
  318.                 // The last piece of content does not have a tab after it.
  319.                 if ($tabNum === $numTabs{
  320.                     break;
  321.                 }
  322.  
  323.                 // Process the tab that comes after the content.
  324.                 $lastCurrColumn $currColumn;
  325.                 $tabNum++;
  326.  
  327.                 // Move the pointer to the next tab stop.
  328.                 if (($currColumn $tabWidth=== 0{
  329.                     // This is the first tab, and we are already at a
  330.                     // tab stop, so this tab counts as a single space.
  331.                     $currColumn++;
  332.                 else {
  333.                     $currColumn++;
  334.                     while (($currColumn $tabWidth!== 0{
  335.                         $currColumn++;
  336.                     }
  337.  
  338.                     $currColumn++;
  339.                 }
  340.  
  341.                 $length     += ($currColumn $lastCurrColumn);
  342.                 $newContent .= $prefix.str_repeat($padding($currColumn $lastCurrColumn - 1));
  343.             }//end foreach
  344.         }//end if
  345.  
  346.         $token['orig_content'$token['content'];
  347.         $token['content']      $newContent;
  348.         $token['length']       $length;
  349.  
  350.     }//end replaceTabsInToken()
  351.  
  352.  
  353.     /**
  354.      * Creates a map of brackets positions.
  355.      *
  356.      * @return void 
  357.      */
  358.     private function createTokenMap()
  359.     {
  360.         if (PHP_CODESNIFFER_VERBOSITY > 1{
  361.             echo "\t*** START TOKEN MAP ***".PHP_EOL;
  362.         }
  363.  
  364.         $squareOpeners   = array();
  365.         $curlyOpeners    = array();
  366.         $this->numTokens count($this->tokens);
  367.  
  368.         $openers   = array();
  369.         $openOwner = null;
  370.  
  371.         for ($i = 0; $i $this->numTokens$i++{
  372.             /*
  373.                 Parenthesis mapping.
  374.             */
  375.  
  376.             if (isset(Util\Tokens::$parenthesisOpeners[$this->tokens[$i]['code']]=== true{
  377.                 $this->tokens[$i]['parenthesis_opener'= null;
  378.                 $this->tokens[$i]['parenthesis_closer'= null;
  379.                 $this->tokens[$i]['parenthesis_owner']  $i;
  380.                 $openOwner $i;
  381.             else if ($this->tokens[$i]['code'=== T_OPEN_PARENTHESIS{
  382.                 $openers[$i;
  383.                 $this->tokens[$i]['parenthesis_opener'$i;
  384.                 if ($openOwner !== null{
  385.                     $this->tokens[$openOwner]['parenthesis_opener'$i;
  386.                     $this->tokens[$i]['parenthesis_owner']          $openOwner;
  387.                     $openOwner = null;
  388.                 }
  389.             else if ($this->tokens[$i]['code'=== T_CLOSE_PARENTHESIS{
  390.                 // Did we set an owner for this set of parenthesis?
  391.                 $numOpeners count($openers);
  392.                 if ($numOpeners !== 0{
  393.                     $opener array_pop($openers);
  394.                     if (isset($this->tokens[$opener]['parenthesis_owner']=== true{
  395.                         $owner $this->tokens[$opener]['parenthesis_owner'];
  396.  
  397.                         $this->tokens[$owner]['parenthesis_closer'$i;
  398.                         $this->tokens[$i]['parenthesis_owner']      $owner;
  399.                     }
  400.  
  401.                     $this->tokens[$i]['parenthesis_opener']      $opener;
  402.                     $this->tokens[$i]['parenthesis_closer']      $i;
  403.                     $this->tokens[$opener]['parenthesis_closer'$i;
  404.                 }
  405.             }//end if
  406.  
  407.             /*
  408.                 Bracket mapping.
  409.             */
  410.  
  411.             switch ($this->tokens[$i]['code']{
  412.             case T_OPEN_SQUARE_BRACKET:
  413.                 $squareOpeners[$i;
  414.  
  415.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  416.                     echo str_repeat("\t"count($squareOpeners));
  417.                     echo str_repeat("\t"count($curlyOpeners));
  418.                     echo "=> Found square bracket opener at $i".PHP_EOL;
  419.                 }
  420.                 break;
  421.             case T_OPEN_CURLY_BRACKET:
  422.                 if (isset($this->tokens[$i]['scope_closer']=== false{
  423.                     $curlyOpeners[$i;
  424.  
  425.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  426.                         echo str_repeat("\t"count($squareOpeners));
  427.                         echo str_repeat("\t"count($curlyOpeners));
  428.                         echo "=> Found curly bracket opener at $i".PHP_EOL;
  429.                     }
  430.                 }
  431.                 break;
  432.             case T_CLOSE_SQUARE_BRACKET:
  433.                 if (empty($squareOpeners=== false{
  434.                     $opener array_pop($squareOpeners);
  435.                     $this->tokens[$i]['bracket_opener']      $opener;
  436.                     $this->tokens[$i]['bracket_closer']      $i;
  437.                     $this->tokens[$opener]['bracket_opener'$opener;
  438.                     $this->tokens[$opener]['bracket_closer'$i;
  439.  
  440.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  441.                         echo str_repeat("\t"count($squareOpeners));
  442.                         echo str_repeat("\t"count($curlyOpeners));
  443.                         echo "\t=> Found square bracket closer at $i for $opener".PHP_EOL;
  444.                     }
  445.                 }
  446.                 break;
  447.             case T_CLOSE_CURLY_BRACKET:
  448.                 if (empty($curlyOpeners=== false
  449.                     && isset($this->tokens[$i]['scope_opener']=== false
  450.                 {
  451.                     $opener array_pop($curlyOpeners);
  452.                     $this->tokens[$i]['bracket_opener']      $opener;
  453.                     $this->tokens[$i]['bracket_closer']      $i;
  454.                     $this->tokens[$opener]['bracket_opener'$opener;
  455.                     $this->tokens[$opener]['bracket_closer'$i;
  456.  
  457.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  458.                         echo str_repeat("\t"count($squareOpeners));
  459.                         echo str_repeat("\t"count($curlyOpeners));
  460.                         echo "\t=> Found curly bracket closer at $i for $opener".PHP_EOL;
  461.                     }
  462.                 }
  463.                 break;
  464.             default:
  465.                 continue;
  466.             }//end switch
  467.         }//end for
  468.  
  469.         // Cleanup for any openers that we didn't find closers for.
  470.         // This typically means there was a syntax error breaking things.
  471.         foreach ($openers as $opener{
  472.             unset($this->tokens[$opener]['parenthesis_opener']);
  473.             unset($this->tokens[$opener]['parenthesis_owner']);
  474.         }
  475.  
  476.         if (PHP_CODESNIFFER_VERBOSITY > 1{
  477.             echo "\t*** END TOKEN MAP ***".PHP_EOL;
  478.         }
  479.  
  480.     }//end createTokenMap()
  481.  
  482.  
  483.     /**
  484.      * Creates a map for the parenthesis tokens that surround other tokens.
  485.      *
  486.      * @return void 
  487.      */
  488.     private function createParenthesisNestingMap()
  489.     {
  490.         $map = array();
  491.         for ($i = 0; $i $this->numTokens$i++{
  492.             if (isset($this->tokens[$i]['parenthesis_opener']=== true
  493.                 && $i === $this->tokens[$i]['parenthesis_opener']
  494.             {
  495.                 if (empty($map=== false{
  496.                     $this->tokens[$i]['nested_parenthesis'$map;
  497.                 }
  498.  
  499.                 if (isset($this->tokens[$i]['parenthesis_closer']=== true{
  500.                     $map[$this->tokens[$i]['parenthesis_opener']]
  501.                         = $this->tokens[$i]['parenthesis_closer'];
  502.                 }
  503.             else if (isset($this->tokens[$i]['parenthesis_closer']=== true
  504.                 && $i === $this->tokens[$i]['parenthesis_closer']
  505.             {
  506.                 array_pop($map);
  507.                 if (empty($map=== false{
  508.                     $this->tokens[$i]['nested_parenthesis'$map;
  509.                 }
  510.             else {
  511.                 if (empty($map=== false{
  512.                     $this->tokens[$i]['nested_parenthesis'$map;
  513.                 }
  514.             }//end if
  515.         }//end for
  516.  
  517.     }//end createParenthesisNestingMap()
  518.  
  519.  
  520.     /**
  521.      * Creates a scope map of tokens that open scopes.
  522.      *
  523.      * @return void 
  524.      * @see    recurseScopeMap()
  525.      */
  526.     private function createScopeMap()
  527.     {
  528.         if (PHP_CODESNIFFER_VERBOSITY > 1{
  529.             echo "\t*** START SCOPE MAP ***".PHP_EOL;
  530.         }
  531.  
  532.         for ($i = 0; $i $this->numTokens$i++{
  533.             // Check to see if the current token starts a new scope.
  534.             if (isset($this->scopeOpeners[$this->tokens[$i]['code']]=== true{
  535.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  536.                     $type    $this->tokens[$i]['type'];
  537.                     $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
  538.                     echo "\tStart scope map at $i:$type => $content".PHP_EOL;
  539.                 }
  540.  
  541.                 if (isset($this->tokens[$i]['scope_condition']=== true{
  542.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  543.                         echo "\t* already processed, skipping *".PHP_EOL;
  544.                     }
  545.  
  546.                     continue;
  547.                 }
  548.  
  549.                 $i $this->recurseScopeMap($i);
  550.             }//end if
  551.         }//end for
  552.  
  553.         if (PHP_CODESNIFFER_VERBOSITY > 1{
  554.             echo "\t*** END SCOPE MAP ***".PHP_EOL;
  555.         }
  556.  
  557.     }//end createScopeMap()
  558.  
  559.  
  560.     /**
  561.      * Recurses though the scope openers to build a scope map.
  562.      *
  563.      * @param int $stackPtr The position in the stack of the token that
  564.      *                       opened the scope (eg. an IF token or FOR token).
  565.      * @param int $depth    How many scope levels down we are.
  566.      * @param int $ignore   How many curly braces we are ignoring.
  567.      *
  568.      * @return int The position in the stack that closed the scope.
  569.      */
  570.     private function recurseScopeMap($stackPtr$depth=1&$ignore=0)
  571.     {
  572.         if (PHP_CODESNIFFER_VERBOSITY > 1{
  573.             echo str_repeat("\t"$depth);
  574.             echo "=> Begin scope map recursion at token $stackPtr with depth $depth".PHP_EOL;
  575.         }
  576.  
  577.         $opener    = null;
  578.         $currType  $this->tokens[$stackPtr]['code'];
  579.         $startLine $this->tokens[$stackPtr]['line'];
  580.  
  581.         // We will need this to restore the value if we end up
  582.         // returning a token ID that causes our calling function to go back
  583.         // over already ignored braces.
  584.         $originalIgnore $ignore;
  585.  
  586.         // If the start token for this scope opener is the same as
  587.         // the scope token, we have already found our opener.
  588.         if (isset($this->scopeOpeners[$currType]['start'][$currType]=== true{
  589.             $opener $stackPtr;
  590.         }
  591.  
  592.         for ($i ($stackPtr + 1)$i $this->numTokens$i++{
  593.             $tokenType $this->tokens[$i]['code'];
  594.  
  595.             if (PHP_CODESNIFFER_VERBOSITY > 1{
  596.                 $type    $this->tokens[$i]['type'];
  597.                 $line    $this->tokens[$i]['line'];
  598.                 $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
  599.  
  600.                 echo str_repeat("\t"$depth);
  601.                 echo "Process token $i on line $line [";
  602.                 if ($opener !== null{
  603.                     echo "opener:$opener;";
  604.                 }
  605.  
  606.                 if ($ignore > 0{
  607.                     echo "ignore=$ignore;";
  608.                 }
  609.  
  610.                 echo "]: $type => $content".PHP_EOL;
  611.             }//end if
  612.  
  613.             // Very special case for IF statements in PHP that can be defined without
  614.             // scope tokens. E.g., if (1) 1; 1 ? (1 ? 1 : 1) : 1;
  615.             // If an IF statement below this one has an opener but no
  616.             // keyword, the opener will be incorrectly assigned to this IF statement.
  617.             // The same case also applies to USE statements, which don't have to have
  618.             // openers, so a following USE statement can cause an incorrect brace match.
  619.             if (($currType === T_IF || $currType === T_ELSE || $currType === T_USE)
  620.                 && $opener === null
  621.                 && ($this->tokens[$i]['code'=== T_SEMICOLON
  622.                 || $this->tokens[$i]['code'=== T_CLOSE_TAG)
  623.             {
  624.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  625.                     $type $this->tokens[$stackPtr]['type'];
  626.                     echo str_repeat("\t"$depth);
  627.                     if ($this->tokens[$i]['code'=== T_SEMICOLON{
  628.                         $closerType 'semicolon';
  629.                     else {
  630.                         $closerType 'close tag';
  631.                     }
  632.  
  633.                     echo "=> Found $closerType before scope opener for $stackPtr:$type, bailing".PHP_EOL;
  634.                 }
  635.  
  636.                 return $i;
  637.             }
  638.  
  639.             // Special case for PHP control structures that have no braces.
  640.             // If we find a curly brace closer before we find the opener,
  641.             // we're not going to find an opener. That closer probably belongs to
  642.             // a control structure higher up.
  643.             if ($opener === null
  644.                 && $ignore === 0
  645.                 && $tokenType === T_CLOSE_CURLY_BRACKET
  646.                 && isset($this->scopeOpeners[$currType]['end'][$tokenType]=== true
  647.             {
  648.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  649.                     $type $this->tokens[$stackPtr]['type'];
  650.                     echo str_repeat("\t"$depth);
  651.                     echo "=> Found curly brace closer before scope opener for $stackPtr:$type, bailing".PHP_EOL;
  652.                 }
  653.  
  654.                 return ($i - 1);
  655.             }
  656.  
  657.             if ($opener !== null
  658.                 && (isset($this->tokens[$i]['scope_opener']=== false
  659.                 || $this->scopeOpeners[$this->tokens[$stackPtr]['code']]['shared'=== true)
  660.                 && isset($this->scopeOpeners[$currType]['end'][$tokenType]=== true
  661.             {
  662.                 if ($ignore > 0 && $tokenType === T_CLOSE_CURLY_BRACKET{
  663.                     // The last opening bracket must have been for a string
  664.                     // offset or alike, so let's ignore it.
  665.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  666.                         echo str_repeat("\t"$depth);
  667.                         echo '* finished ignoring curly brace *'.PHP_EOL;
  668.                     }
  669.  
  670.                     $ignore--;
  671.                     continue;
  672.                 else if ($this->tokens[$opener]['code'=== T_OPEN_CURLY_BRACKET
  673.                     && $tokenType !== T_CLOSE_CURLY_BRACKET
  674.                 {
  675.                     // The opener is a curly bracket so the closer must be a curly bracket as well.
  676.                     // We ignore this closer to handle cases such as T_ELSE or T_ELSEIF being considered
  677.                     // a closer of T_IF when it should not.
  678.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  679.                         $type $this->tokens[$stackPtr]['type'];
  680.                         echo str_repeat("\t"$depth);
  681.                         echo "=> Ignoring non-curly scope closer for $stackPtr:$type".PHP_EOL;
  682.                     }
  683.                 else {
  684.                     $scopeCloser $i;
  685.                     $todo        = array(
  686.                                     $stackPtr,
  687.                                     $opener,
  688.                                    );
  689.  
  690.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  691.                         $type       $this->tokens[$stackPtr]['type'];
  692.                         $closerType $this->tokens[$scopeCloser]['type'];
  693.                         echo str_repeat("\t"$depth);
  694.                         echo "=> Found scope closer ($scopeCloser:$closerType) for $stackPtr:$type".PHP_EOL;
  695.                     }
  696.  
  697.                     $validCloser = true;
  698.                     if (($this->tokens[$stackPtr]['code'=== T_IF || $this->tokens[$stackPtr]['code'=== T_ELSEIF)
  699.                         && ($tokenType === T_ELSE || $tokenType === T_ELSEIF)
  700.                     {
  701.                         // To be a closer, this token must have an opener.
  702.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  703.                             echo str_repeat("\t"$depth);
  704.                             echo "* closer needs to be tested *".PHP_EOL;
  705.                         }
  706.  
  707.                         $i = self::recurseScopeMap($i($depth + 1)$ignore);
  708.  
  709.                         if (isset($this->tokens[$scopeCloser]['scope_opener']=== false{
  710.                             $validCloser = false;
  711.                             if (PHP_CODESNIFFER_VERBOSITY > 1{
  712.                                 echo str_repeat("\t"$depth);
  713.                                 echo "* closer is not valid (no opener found) *".PHP_EOL;
  714.                             }
  715.                         else if ($this->tokens[$this->tokens[$scopeCloser]['scope_opener']]['code'!== $this->tokens[$opener]['code']{
  716.                             $validCloser = false;
  717.                             if (PHP_CODESNIFFER_VERBOSITY > 1{
  718.                                 echo str_repeat("\t"$depth);
  719.                                 $type       $this->tokens[$this->tokens[$scopeCloser]['scope_opener']]['type'];
  720.                                 $openerType $this->tokens[$opener]['type'];
  721.                                 echo "* closer is not valid (mismatched opener type; $type != $openerType) *".PHP_EOL;
  722.                             }
  723.                         else if (PHP_CODESNIFFER_VERBOSITY > 1{
  724.                             echo str_repeat("\t"$depth);
  725.                             echo "* closer was valid *".PHP_EOL;
  726.                         }
  727.                     else {
  728.                         // The closer was not processed, so we need to
  729.                         // complete that token as well.
  730.                         $todo[$scopeCloser;
  731.                     }//end if
  732.  
  733.                     if ($validCloser === true{
  734.                         foreach ($todo as $token{
  735.                             $this->tokens[$token]['scope_condition'$stackPtr;
  736.                             $this->tokens[$token]['scope_opener']    $opener;
  737.                             $this->tokens[$token]['scope_closer']    $scopeCloser;
  738.                         }
  739.  
  740.                         if ($this->scopeOpeners[$this->tokens[$stackPtr]['code']]['shared'=== true{
  741.                             // As we are going back to where we started originally, restore
  742.                             // the ignore value back to its original value.
  743.                             $ignore $originalIgnore;
  744.                             return $opener;
  745.                         else if ($scopeCloser === $i
  746.                             && isset($this->scopeOpeners[$tokenType]=== true
  747.                         {
  748.                             // Unset scope_condition here or else the token will appear to have
  749.                             // already been processed, and it will be skipped. Normally we want that,
  750.                             // but in this case, the token is both a closer and an opener, so
  751.                             // it needs to act like an opener. This is also why we return the
  752.                             // token before this one; so the closer has a chance to be processed
  753.                             // a second time, but as an opener.
  754.                             unset($this->tokens[$scopeCloser]['scope_condition']);
  755.                             return ($i - 1);
  756.                         else {
  757.                             return $i;
  758.                         }
  759.                     else {
  760.                         continue;
  761.                     }//end if
  762.                 }//end if
  763.             }//end if
  764.  
  765.             // Is this an opening condition ?
  766.             if (isset($this->scopeOpeners[$tokenType]=== true{
  767.                 if ($opener === null{
  768.                     if ($tokenType === T_USE{
  769.                         // PHP use keywords are special because they can be
  770.                         // used as blocks but also inline in function definitions.
  771.                         // So if we find them nested inside another opener, just skip them.
  772.                         continue;
  773.                     }
  774.  
  775.                     if ($tokenType === T_FUNCTION
  776.                         && $this->tokens[$stackPtr]['code'!== T_FUNCTION
  777.                     {
  778.                         // Probably a closure, so process it manually.
  779.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  780.                             $type $this->tokens[$stackPtr]['type'];
  781.                             echo str_repeat("\t"$depth);
  782.                             echo "=> Found function before scope opener for $stackPtr:$type, processing manually".PHP_EOL;
  783.                         }
  784.  
  785.                         if (isset($this->tokens[$i]['scope_closer']=== true{
  786.                             // We've already processed this closure.
  787.                             if (PHP_CODESNIFFER_VERBOSITY > 1{
  788.                                 echo str_repeat("\t"$depth);
  789.                                 echo '* already processed, skipping *'.PHP_EOL;
  790.                             }
  791.  
  792.                             $i $this->tokens[$i]['scope_closer'];
  793.                             continue;
  794.                         }
  795.  
  796.                         $i = self::recurseScopeMap($i($depth + 1)$ignore);
  797.                         continue;
  798.                     }//end if
  799.  
  800.                     // Found another opening condition but still haven't
  801.                     // found our opener, so we are never going to find one.
  802.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  803.                         $type $this->tokens[$stackPtr]['type'];
  804.                         echo str_repeat("\t"$depth);
  805.                         echo "=> Found new opening condition before scope opener for $stackPtr:$type";
  806.                     }
  807.  
  808.                     if (($this->tokens[$stackPtr]['code'=== T_IF
  809.                         || $this->tokens[$stackPtr]['code'=== T_ELSEIF
  810.                         || $this->tokens[$stackPtr]['code'=== T_ELSE)
  811.                         && ($this->tokens[$i]['code'=== T_ELSE
  812.                         || $this->tokens[$i]['code'=== T_ELSEIF)
  813.                     {
  814.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  815.                             echo "continuing".PHP_EOL;
  816.                         }
  817.  
  818.                         return ($i - 1);
  819.                     else {
  820.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  821.                             echo "backtracking".PHP_EOL;
  822.                         }
  823.  
  824.                         return $stackPtr;
  825.                     }
  826.                 }//end if
  827.  
  828.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  829.                     echo str_repeat("\t"$depth);
  830.                     echo '* token is an opening condition *'.PHP_EOL;
  831.                 }
  832.  
  833.                 $isShared ($this->scopeOpeners[$tokenType]['shared'=== true);
  834.  
  835.                 if (isset($this->tokens[$i]['scope_condition']=== true{
  836.                     // We've been here before.
  837.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  838.                         echo str_repeat("\t"$depth);
  839.                         echo '* already processed, skipping *'.PHP_EOL;
  840.                     }
  841.  
  842.                     if ($isShared === false
  843.                         && isset($this->tokens[$i]['scope_closer']=== true
  844.                     {
  845.                         $i $this->tokens[$i]['scope_closer'];
  846.                     }
  847.  
  848.                     continue;
  849.                 else if ($currType === $tokenType
  850.                     && $isShared === false
  851.                     && $opener === null
  852.                 {
  853.                     // We haven't yet found our opener, but we have found another
  854.                     // scope opener which is the same type as us, and we don't
  855.                     // share openers, so we will never find one.
  856.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  857.                         echo str_repeat("\t"$depth);
  858.                         echo '* it was another token\'s opener, bailing *'.PHP_EOL;
  859.                     }
  860.  
  861.                     return $stackPtr;
  862.                 else {
  863.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  864.                         echo str_repeat("\t"$depth);
  865.                         echo '* searching for opener *'.PHP_EOL;
  866.                     }
  867.  
  868.                     if (isset($this->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]=== true{
  869.                         $oldIgnore $ignore;
  870.                         $ignore    = 0;
  871.                     }
  872.  
  873.                     // PHP has a max nesting level for functions. Stop before we hit that limit
  874.                     // because too many loops means we've run into trouble anyway.
  875.                     if ($depth > 50{
  876.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  877.                             echo str_repeat("\t"$depth);
  878.                             echo '* reached maximum nesting level; aborting *'.PHP_EOL;
  879.                         }
  880.  
  881.                         throw new RuntimeException('Maximum nesting level reached; file could not be processed');
  882.                     }
  883.  
  884.                     $oldDepth $depth;
  885.                     if ($isShared === true
  886.                         && isset($this->scopeOpeners[$tokenType]['with'][$currType]=== true
  887.                     {
  888.                         // Don't allow the depth to increment because this is
  889.                         // possibly not a true nesting if we are sharing our closer.
  890.                         // This can happen, for example, when a SWITCH has a large
  891.                         // number of CASE statements with the same shared BREAK.
  892.                         $depth--;
  893.                     }
  894.  
  895.                     $i     = self::recurseScopeMap($i($depth + 1)$ignore);
  896.                     $depth $oldDepth;
  897.  
  898.                     if (isset($this->scopeOpeners[$tokenType]['end'][T_CLOSE_CURLY_BRACKET]=== true{
  899.                         $ignore $oldIgnore;
  900.                     }
  901.                 }//end if
  902.             }//end if
  903.  
  904.             if (isset($this->scopeOpeners[$currType]['start'][$tokenType]=== true
  905.                 && $opener === null
  906.             {
  907.                 if ($tokenType === T_OPEN_CURLY_BRACKET{
  908.                     if (isset($this->tokens[$stackPtr]['parenthesis_closer']=== true
  909.                         && $i $this->tokens[$stackPtr]['parenthesis_closer']
  910.                     {
  911.                         // We found a curly brace inside the condition of the
  912.                         // current scope opener, so it must be a string offset.
  913.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  914.                             echo str_repeat("\t"$depth);
  915.                             echo '* ignoring curly brace inside condition *'.PHP_EOL;
  916.                         }
  917.  
  918.                         $ignore++;
  919.                     else {
  920.                         // Make sure this is actually an opener and not a
  921.                         // string offset (e.g., $var{0}).
  922.                         for ($x ($i - 1)$x > 0; $x--{
  923.                             if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]=== true{
  924.                                 continue;
  925.                             else {
  926.                                 // If the first non-whitespace/comment token looks like this
  927.                                 // brace is a string offset, or this brace is mid-way through
  928.                                 // a new statement, it isn't a scope opener.
  929.                                 $disallowed  = Util\Tokens::$assignmentTokens;
  930.                                 $disallowed += array(
  931.                                                 T_VARIABLE         => true,
  932.                                                 T_OBJECT_OPERATOR  => true,
  933.                                                 T_COMMA            => true,
  934.                                                 T_OPEN_PARENTHESIS => true,
  935.                                                );
  936.  
  937.                                 if (isset($disallowed[$this->tokens[$x]['code']]=== true{
  938.                                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  939.                                         echo str_repeat("\t"$depth);
  940.                                         echo '* ignoring curly brace *'.PHP_EOL;
  941.                                     }
  942.  
  943.                                     $ignore++;
  944.                                 }
  945.  
  946.                                 break;
  947.                             }//end if
  948.                         }//end for
  949.                     }//end if
  950.                 }//end if
  951.  
  952.                 if ($ignore === 0 || $tokenType !== T_OPEN_CURLY_BRACKET{
  953.                     // We found the opening scope token for $currType.
  954.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  955.                         $type $this->tokens[$stackPtr]['type'];
  956.                         echo str_repeat("\t"$depth);
  957.                         echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
  958.                     }
  959.  
  960.                     $opener $i;
  961.                 }
  962.             else if ($tokenType === T_OPEN_PARENTHESIS{
  963.                 if (isset($this->tokens[$i]['parenthesis_owner']=== true{
  964.                     $owner $this->tokens[$i]['parenthesis_owner'];
  965.                     if (isset(Util\Tokens::$scopeOpeners[$this->tokens[$owner]['code']]=== true
  966.                         && isset($this->tokens[$i]['parenthesis_closer']=== true
  967.                     {
  968.                         // If we get into here, then we opened a parenthesis for
  969.                         // a scope (eg. an if or else if) so we need to update the
  970.                         // start of the line so that when we check to see
  971.                         // if the closing parenthesis is more than 3 lines away from
  972.                         // the statement, we check from the closing parenthesis.
  973.                         $startLine $this->tokens[$this->tokens[$i]['parenthesis_closer']]['line'];
  974.                     }
  975.                 }
  976.             else if ($tokenType === T_OPEN_CURLY_BRACKET && $opener !== null{
  977.                 // We opened something that we don't have a scope opener for.
  978.                 // Examples of this are curly brackets for string offsets etc.
  979.                 // We want to ignore this so that we don't have an invalid scope
  980.                 // map.
  981.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  982.                     echo str_repeat("\t"$depth);
  983.                     echo '* ignoring curly brace *'.PHP_EOL;
  984.                 }
  985.  
  986.                 $ignore++;
  987.             else if ($tokenType === T_CLOSE_CURLY_BRACKET && $ignore > 0{
  988.                 // We found the end token for the opener we were ignoring.
  989.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  990.                     echo str_repeat("\t"$depth);
  991.                     echo '* finished ignoring curly brace *'.PHP_EOL;
  992.                 }
  993.  
  994.                 $ignore--;
  995.             else if ($opener === null
  996.                 && isset($this->scopeOpeners[$currType]=== true
  997.             {
  998.                 // If we still haven't found the opener after 3 lines,
  999.                 // we're not going to find it, unless we know it requires
  1000.                 // an opener (in which case we better keep looking) or the last
  1001.                 // token was empty (in which case we'll just confirm there is
  1002.                 // more code in this file and not just a big comment).
  1003.                 if ($this->tokens[$i]['line'>= ($startLine + 3)
  1004.                     && isset(Util\Tokens::$emptyTokens[$this->tokens[($i - 1)]['code']]=== false
  1005.                 {
  1006.                     if ($this->scopeOpeners[$currType]['strict'=== true{
  1007.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  1008.                             $type  $this->tokens[$stackPtr]['type'];
  1009.                             $lines ($this->tokens[$i]['line'$startLine);
  1010.                             echo str_repeat("\t"$depth);
  1011.                             echo "=> Still looking for $stackPtr:$type scope opener after $lines lines".PHP_EOL;
  1012.                         }
  1013.                     else {
  1014.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  1015.                             $type $this->tokens[$stackPtr]['type'];
  1016.                             echo str_repeat("\t"$depth);
  1017.                             echo "=> Couldn't find scope opener for $stackPtr:$type, bailing".PHP_EOL;
  1018.                         }
  1019.  
  1020.                         return $stackPtr;
  1021.                     }
  1022.                 }
  1023.             else if ($opener !== null
  1024.                 && $tokenType !== T_BREAK
  1025.                 && isset($this->endScopeTokens[$tokenType]=== true
  1026.             {
  1027.                 if (isset($this->tokens[$i]['scope_condition']=== false{
  1028.                     if ($ignore > 0{
  1029.                         // We found the end token for the opener we were ignoring.
  1030.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  1031.                             echo str_repeat("\t"$depth);
  1032.                             echo '* finished ignoring curly brace *'.PHP_EOL;
  1033.                         }
  1034.  
  1035.                         $ignore--;
  1036.                     else {
  1037.                         // We found a token that closes the scope but it doesn't
  1038.                         // have a condition, so it belongs to another token and
  1039.                         // our token doesn't have a closer, so pretend this is
  1040.                         // the closer.
  1041.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  1042.                             $type $this->tokens[$stackPtr]['type'];
  1043.                             echo str_repeat("\t"$depth);
  1044.                             echo "=> Found (unexpected) scope closer for $stackPtr:$type".PHP_EOL;
  1045.                         }
  1046.  
  1047.                         foreach (array($stackPtr$openeras $token{
  1048.                             $this->tokens[$token]['scope_condition'$stackPtr;
  1049.                             $this->tokens[$token]['scope_opener']    $opener;
  1050.                             $this->tokens[$token]['scope_closer']    $i;
  1051.                         }
  1052.  
  1053.                         return ($i - 1);
  1054.                     }//end if
  1055.                 }//end if
  1056.             }//end if
  1057.         }//end for
  1058.  
  1059.         return $stackPtr;
  1060.  
  1061.     }//end recurseScopeMap()
  1062.  
  1063.  
  1064.     /**
  1065.      * Constructs the level map.
  1066.      *
  1067.      * The level map adds a 'level' index to each token which indicates the
  1068.      * depth that a token within a set of scope blocks. It also adds a
  1069.      * 'condition' index which is an array of the scope conditions that opened
  1070.      * each of the scopes - position 0 being the first scope opener.
  1071.      *
  1072.      * @return void 
  1073.      */
  1074.     private function createLevelMap()
  1075.     {
  1076.         if (PHP_CODESNIFFER_VERBOSITY > 1{
  1077.             echo "\t*** START LEVEL MAP ***".PHP_EOL;
  1078.         }
  1079.  
  1080.         $this->numTokens count($this->tokens);
  1081.         $level           = 0;
  1082.         $conditions      = array();
  1083.         $lastOpener      = null;
  1084.         $openers         = array();
  1085.  
  1086.         for ($i = 0; $i $this->numTokens$i++{
  1087.             if (PHP_CODESNIFFER_VERBOSITY > 1{
  1088.                 $type $this->tokens[$i]['type'];
  1089.                 $line $this->tokens[$i]['line'];
  1090.                 $len  $this->tokens[$i]['length'];
  1091.                 $col  $this->tokens[$i]['column'];
  1092.  
  1093.                 $content = Util\Common::prepareForOutput($this->tokens[$i]['content']);
  1094.  
  1095.                 echo str_repeat("\t"($level + 1));
  1096.                 echo "Process token $i on line $line [col:$col;len:$len;lvl:$level;";
  1097.                 if (empty($conditions!== true{
  1098.                     $condString 'conds;';
  1099.                     foreach ($conditions as $condition{
  1100.                         $condString .= Util\Tokens::tokenName($condition).',';
  1101.                     }
  1102.  
  1103.                     echo rtrim($condString',').';';
  1104.                 }
  1105.  
  1106.                 echo "]: $type => $content".PHP_EOL;
  1107.             }//end if
  1108.  
  1109.             $this->tokens[$i]['level']      $level;
  1110.             $this->tokens[$i]['conditions'$conditions;
  1111.  
  1112.             if (isset($this->tokens[$i]['scope_condition']=== true{
  1113.                 // Check to see if this token opened the scope.
  1114.                 if ($this->tokens[$i]['scope_opener'=== $i{
  1115.                     $stackPtr $this->tokens[$i]['scope_condition'];
  1116.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  1117.                         $type $this->tokens[$stackPtr]['type'];
  1118.                         echo str_repeat("\t"($level + 1));
  1119.                         echo "=> Found scope opener for $stackPtr:$type".PHP_EOL;
  1120.                     }
  1121.  
  1122.                     $stackPtr $this->tokens[$i]['scope_condition'];
  1123.  
  1124.                     // If we find a scope opener that has a shared closer,
  1125.                     // then we need to go back over the condition map that we
  1126.                     // just created and fix ourselves as we just added some
  1127.                     // conditions where there was none. This happens for T_CASE
  1128.                     // statements that are using the same break statement.
  1129.                     if ($lastOpener !== null && $this->tokens[$lastOpener]['scope_closer'=== $this->tokens[$i]['scope_closer']{
  1130.                         // This opener shares its closer with the previous opener,
  1131.                         // but we still need to check if the two openers share their
  1132.                         // closer with each other directly (like CASE and DEFAULT)
  1133.                         // or if they are just sharing because one doesn't have a
  1134.                         // closer (like CASE with no BREAK using a SWITCHes closer).
  1135.                         $thisType $this->tokens[$this->tokens[$i]['scope_condition']]['code'];
  1136.                         $opener   $this->tokens[$lastOpener]['scope_condition'];
  1137.  
  1138.                         $isShared = isset($this->scopeOpeners[$thisType]['with'][$this->tokens[$opener]['code']]);
  1139.  
  1140.                         reset($this->scopeOpeners[$thisType]['end']);
  1141.                         reset($this->scopeOpeners[$this->tokens[$opener]['code']]['end']);
  1142.                         $sameEnd (current($this->scopeOpeners[$thisType]['end']=== current($this->scopeOpeners[$this->tokens[$opener]['code']]['end']));
  1143.  
  1144.                         if ($isShared === true && $sameEnd === true{
  1145.                             $badToken $opener;
  1146.                             if (PHP_CODESNIFFER_VERBOSITY > 1{
  1147.                                 $type $this->tokens[$badToken]['type'];
  1148.                                 echo str_repeat("\t"($level + 1));
  1149.                                 echo "* shared closer, cleaning up $badToken:$type *".PHP_EOL;
  1150.                             }
  1151.  
  1152.                             for ($x $this->tokens[$i]['scope_condition']$x <= $i$x++{
  1153.                                 $oldConditions $this->tokens[$x]['conditions'];
  1154.                                 $oldLevel      $this->tokens[$x]['level'];
  1155.                                 $this->tokens[$x]['level']--;
  1156.                                 unset($this->tokens[$x]['conditions'][$badToken]);
  1157.                                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  1158.                                     $type     $this->tokens[$x]['type'];
  1159.                                     $oldConds '';
  1160.                                     foreach ($oldConditions as $condition{
  1161.                                         $oldConds .= Util\Tokens::tokenName($condition).',';
  1162.                                     }
  1163.  
  1164.                                     $oldConds rtrim($oldConds',');
  1165.  
  1166.                                     $newConds '';
  1167.                                     foreach ($this->tokens[$x]['conditions'as $condition{
  1168.                                         $newConds .= Util\Tokens::tokenName($condition).',';
  1169.                                     }
  1170.  
  1171.                                     $newConds rtrim($newConds',');
  1172.  
  1173.                                     $newLevel $this->tokens[$x]['level'];
  1174.                                     echo str_repeat("\t"($level + 1));
  1175.                                     echo "* cleaned $x:$type *".PHP_EOL;
  1176.                                     echo str_repeat("\t"($level + 2));
  1177.                                     echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
  1178.                                     echo str_repeat("\t"($level + 2));
  1179.                                     echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
  1180.                                 }//end if
  1181.                             }//end for
  1182.  
  1183.                             unset($conditions[$badToken]);
  1184.                             if (PHP_CODESNIFFER_VERBOSITY > 1{
  1185.                                 $type $this->tokens[$badToken]['type'];
  1186.                                 echo str_repeat("\t"($level + 1));
  1187.                                 echo "* token $badToken:$type removed from conditions array *".PHP_EOL;
  1188.                             }
  1189.  
  1190.                             unset($openers[$lastOpener]);
  1191.  
  1192.                             $level--;
  1193.                             if (PHP_CODESNIFFER_VERBOSITY > 1{
  1194.                                 echo str_repeat("\t"($level + 2));
  1195.                                 echo '* level decreased *'.PHP_EOL;
  1196.                             }
  1197.                         }//end if
  1198.                     }//end if
  1199.  
  1200.                     $level++;
  1201.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  1202.                         echo str_repeat("\t"($level + 1));
  1203.                         echo '* level increased *'.PHP_EOL;
  1204.                     }
  1205.  
  1206.                     $conditions[$stackPtr$this->tokens[$stackPtr]['code'];
  1207.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  1208.                         $type $this->tokens[$stackPtr]['type'];
  1209.                         echo str_repeat("\t"($level + 1));
  1210.                         echo "* token $stackPtr:$type added to conditions array *".PHP_EOL;
  1211.                     }
  1212.  
  1213.                     $lastOpener $this->tokens[$i]['scope_opener'];
  1214.                     if ($lastOpener !== null{
  1215.                         $openers[$lastOpener$lastOpener;
  1216.                     }
  1217.                 else if ($lastOpener !== null && $this->tokens[$lastOpener]['scope_closer'=== $i{
  1218.                     foreach (array_reverse($openersas $opener{
  1219.                         if ($this->tokens[$opener]['scope_closer'=== $i{
  1220.                             $oldOpener array_pop($openers);
  1221.                             if (empty($openers=== false{
  1222.                                 $lastOpener           array_pop($openers);
  1223.                                 $openers[$lastOpener$lastOpener;
  1224.                             else {
  1225.                                 $lastOpener = null;
  1226.                             }
  1227.  
  1228.                             if (PHP_CODESNIFFER_VERBOSITY > 1{
  1229.                                 $type $this->tokens[$oldOpener]['type'];
  1230.                                 echo str_repeat("\t"($level + 1));
  1231.                                 echo "=> Found scope closer for $oldOpener:$type".PHP_EOL;
  1232.                             }
  1233.  
  1234.                             $oldCondition array_pop($conditions);
  1235.                             if (PHP_CODESNIFFER_VERBOSITY > 1{
  1236.                                 echo str_repeat("\t"($level + 1));
  1237.                                 echo '* token '.Util\Tokens::tokenName($oldCondition).' removed from conditions array *'.PHP_EOL;
  1238.                             }
  1239.  
  1240.                             // Make sure this closer actually belongs to us.
  1241.                             // Either the condition also has to think this is the
  1242.                             // closer, or it has to allow sharing with us.
  1243.                             $condition $this->tokens[$this->tokens[$i]['scope_condition']]['code'];
  1244.                             if ($condition !== $oldCondition{
  1245.                                 if (isset($this->scopeOpeners[$oldCondition]['with'][$condition]=== false{
  1246.                                     $badToken $this->tokens[$oldOpener]['scope_condition'];
  1247.  
  1248.                                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  1249.                                         $type = Util\Tokens::tokenName($oldCondition);
  1250.                                         echo str_repeat("\t"($level + 1));
  1251.                                         echo "* scope closer was bad, cleaning up $badToken:$type *".PHP_EOL;
  1252.                                     }
  1253.  
  1254.                                     for ($x ($oldOpener + 1)$x <= $i$x++{
  1255.                                         $oldConditions $this->tokens[$x]['conditions'];
  1256.                                         $oldLevel      $this->tokens[$x]['level'];
  1257.                                         $this->tokens[$x]['level']--;
  1258.                                         unset($this->tokens[$x]['conditions'][$badToken]);
  1259.                                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  1260.                                             $type     $this->tokens[$x]['type'];
  1261.                                             $oldConds '';
  1262.                                             foreach ($oldConditions as $condition{
  1263.                                                 $oldConds .= Util\Tokens::tokenName($condition).',';
  1264.                                             }
  1265.  
  1266.                                             $oldConds rtrim($oldConds',');
  1267.  
  1268.                                             $newConds '';
  1269.                                             foreach ($this->tokens[$x]['conditions'as $condition{
  1270.                                                 $newConds .= Util\Tokens::tokenName($condition).',';
  1271.                                             }
  1272.  
  1273.                                             $newConds rtrim($newConds',');
  1274.  
  1275.                                             $newLevel $this->tokens[$x]['level'];
  1276.                                             echo str_repeat("\t"($level + 1));
  1277.                                             echo "* cleaned $x:$type *".PHP_EOL;
  1278.                                             echo str_repeat("\t"($level + 2));
  1279.                                             echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
  1280.                                             echo str_repeat("\t"($level + 2));
  1281.                                             echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
  1282.                                         }//end if
  1283.                                     }//end for
  1284.                                 }//end if
  1285.                             }//end if
  1286.  
  1287.                             $level--;
  1288.                             if (PHP_CODESNIFFER_VERBOSITY > 1{
  1289.                                 echo str_repeat("\t"($level + 2));
  1290.                                 echo '* level decreased *'.PHP_EOL;
  1291.                             }
  1292.  
  1293.                             $this->tokens[$i]['level']      $level;
  1294.                             $this->tokens[$i]['conditions'$conditions;
  1295.                         }//end if
  1296.                     }//end foreach
  1297.                 }//end if
  1298.             }//end if
  1299.         }//end for
  1300.  
  1301.         if (PHP_CODESNIFFER_VERBOSITY > 1{
  1302.             echo "\t*** END LEVEL MAP ***".PHP_EOL;
  1303.         }
  1304.  
  1305.     }//end createLevelMap()
  1306.  
  1307.  
  1308. }//end class

Documentation generated on Mon, 11 Mar 2019 15:27:49 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.