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

Documentation generated on Mon, 11 Mar 2019 14:50:01 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.