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

Source for file Ruleset.php

Documentation is available at Ruleset.php

  1. <?php
  2. /**
  3.  * Stores the rules used to check and fix files.
  4.  *
  5.  * A ruleset object directly maps to a ruleset XML file.
  6.  *
  7.  * @author    Greg Sherwood <gsherwood@squiz.net>
  8.  * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
  9.  * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
  10.  */
  11.  
  12. namespace PHP_CodeSniffer;
  13.  
  14. use PHP_CodeSniffer\Util;
  15. use PHP_CodeSniffer\Exceptions\RuntimeException;
  16.  
  17. class Ruleset
  18. {
  19.  
  20.     /**
  21.      * The name of the coding standard being used.
  22.      *
  23.      * If a top-level standard includes other standards, or sniffs
  24.      * from other standards, only the name of the top-level standard
  25.      * will be stored in here.
  26.      *
  27.      * If multiple top-level standards are being loaded into
  28.      * a single ruleset object, this will store a comma separated list
  29.      * of the top-level standard names.
  30.      *
  31.      * @var string 
  32.      */
  33.     public $name '';
  34.  
  35.     /**
  36.      * A list of file paths for the ruleset files being used.
  37.      *
  38.      * @var string[] 
  39.      */
  40.     public $paths '';
  41.  
  42.     /**
  43.      * A list of regular expressions used to ignore specific sniffs for files and folders.
  44.      *
  45.      * Is also used to set global exclude patterns.
  46.      * The key is the regular expression and the value is the type
  47.      * of ignore pattern (absolute or relative).
  48.      *
  49.      * @var array<string, string>
  50.      */
  51.     public $ignorePatterns = array();
  52.  
  53.     /**
  54.      * A list of regular expressions used to include specific sniffs for files and folders.
  55.      *
  56.      * The key is the sniff code and the value is an array with
  57.      * the key being a regular expression and the value is the type
  58.      * of ignore pattern (absolute or relative).
  59.      *
  60.      * @var array<string, array<string, string>>
  61.      */
  62.     public $includePatterns = array();
  63.  
  64.     /**
  65.      * An array of sniff objects that are being used to check files.
  66.      *
  67.      * The key is the fully qualified name of the sniff class
  68.      * and the value is the sniff object.
  69.      *
  70.      * @var array<string, \PHP_CodeSniffer\Sniff>
  71.      */
  72.     public $sniffs = array();
  73.  
  74.     /**
  75.      * A mapping of sniff codes to fully qualified class names.
  76.      *
  77.      * The key is the sniff code and the value
  78.      * is the fully qualified name of the sniff class.
  79.      *
  80.      * @var array<string, string>
  81.      */
  82.     public $sniffCodes = array();
  83.  
  84.     /**
  85.      * An array of token types and the sniffs that are listening for them.
  86.      *
  87.      * The key is the token name being listened for and the value
  88.      * is the sniff object.
  89.      *
  90.      * @var array<int, \PHP_CodeSniffer\Sniff>
  91.      */
  92.     public $tokenListeners = array();
  93.  
  94.     /**
  95.      * An array of rules from the ruleset.xml file.
  96.      *
  97.      * It may be empty, indicating that the ruleset does not override
  98.      * any of the default sniff settings.
  99.      *
  100.      * @var array<string, mixed>
  101.      */
  102.     public $ruleset = array();
  103.  
  104.     /**
  105.      * The directories that the processed rulesets are in.
  106.      *
  107.      * @var string[] 
  108.      */
  109.     protected $rulesetDirs = array();
  110.  
  111.     /**
  112.      * The config data for the run.
  113.      *
  114.      * @var \PHP_CodeSniffer\Config 
  115.      */
  116.     private $config = null;
  117.  
  118.  
  119.     /**
  120.      * Initialise the ruleset that the run will use.
  121.      *
  122.      * @param \PHP_CodeSniffer\Config $config The config data for the run.
  123.      *
  124.      * @return void 
  125.      */
  126.     public function __construct(Config $config)
  127.     {
  128.         // Ignore sniff restrictions if caching is on.
  129.         $restrictions = array();
  130.         $exclusions   = array();
  131.         if ($config->cache === false{
  132.             $restrictions $config->sniffs;
  133.             $exclusions   $config->exclude;
  134.         }
  135.  
  136.         $this->config $config;
  137.  
  138.         $sniffs = array();
  139.         foreach ($config->standards as $standard{
  140.             $installed = Util\Standards::getInstalledStandardPath($standard);
  141.             if ($installed === null{
  142.                 $standard = Util\Common::realpath($standard);
  143.                 if (is_dir($standard=== true
  144.                     && is_file(Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml')) === true
  145.                 {
  146.                     $standard = Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml');
  147.                 }
  148.             else {
  149.                 $standard $installed;
  150.             }
  151.  
  152.             $ruleset simplexml_load_file($standard);
  153.             if ($ruleset !== false{
  154.                 $standardName = (string) $ruleset['name'];
  155.                 if ($this->name !== ''{
  156.                     $this->name .= ', ';
  157.                 }
  158.  
  159.                 $this->name   .= $standardName;
  160.                 $this->paths[$standard;
  161.             }
  162.  
  163.             if (PHP_CODESNIFFER_VERBOSITY === 1{
  164.                 echo "Registering sniffs in the $standardName standard... ";
  165.                 if (count($config->standards> 1 || PHP_CODESNIFFER_VERBOSITY > 2{
  166.                     echo PHP_EOL;
  167.                 }
  168.             }
  169.  
  170.             $sniffs array_merge($sniffs$this->processRuleset($standard));
  171.         }//end foreach
  172.  
  173.         $sniffRestrictions = array();
  174.         foreach ($restrictions as $sniffCode{
  175.             $parts     explode('.'strtolower($sniffCode));
  176.             $sniffName 'php_codesniffer\standards\\'.$parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff';
  177.             $sniffRestrictions[$sniffName= true;
  178.         }
  179.  
  180.         $sniffExclusions = array();
  181.         foreach ($exclusions as $sniffCode{
  182.             $parts     explode('.'strtolower($sniffCode));
  183.             $sniffName 'php_codesniffer\standards\\'.$parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff';
  184.             $sniffExclusions[$sniffName= true;
  185.         }
  186.  
  187.         $this->registerSniffs($sniffs$sniffRestrictions$sniffExclusions);
  188.         $this->populateTokenListeners();
  189.  
  190.         if (PHP_CODESNIFFER_VERBOSITY === 1{
  191.             $numSniffs count($this->sniffs);
  192.             echo "DONE ($numSniffs sniffs registered)".PHP_EOL;
  193.         }
  194.  
  195.     }//end __construct()
  196.  
  197.  
  198.     /**
  199.      * Prints a report showing the sniffs contained in a standard.
  200.      *
  201.      * @return void 
  202.      */
  203.     public function explain()
  204.     {
  205.         $sniffs array_keys($this->sniffs);
  206.         sort($sniffs);
  207.  
  208.         ob_start();
  209.  
  210.         $lastStandard = null;
  211.         $lastCount    '';
  212.         $sniffCount   count($sniffs);
  213.  
  214.         // Add a dummy entry to the end so we loop
  215.         // one last time and clear the output buffer.
  216.         $sniffs['';
  217.  
  218.         echo PHP_EOL."The $this->name standard contains $sniffCount sniffs".PHP_EOL;
  219.  
  220.         ob_start();
  221.  
  222.         foreach ($sniffs as $i => $sniff{
  223.             if ($i === $sniffCount{
  224.                 $currentStandard = null;
  225.             else {
  226.                 $parts explode('\\'$sniff);
  227.  
  228.                 $currentStandard $parts[2];
  229.                 if ($lastStandard === null{
  230.                     $lastStandard $currentStandard;
  231.                 }
  232.             }
  233.  
  234.             if ($currentStandard !== $lastStandard{
  235.                 $sniffList ob_get_contents();
  236.                 ob_end_clean();
  237.  
  238.                 echo PHP_EOL.$lastStandard.' ('.$lastCount.' sniffs)'.PHP_EOL;
  239.                 echo str_repeat('-'(strlen($lastStandard.$lastCount+ 10));
  240.                 echo PHP_EOL;
  241.                 echo $sniffList;
  242.  
  243.                 $lastStandard $parts[2];
  244.                 $lastCount    = 0;
  245.  
  246.                 if ($currentStandard === null{
  247.                     break;
  248.                 }
  249.  
  250.                 ob_start();
  251.             }
  252.  
  253.             echo '  '.$parts[2].'.'.$parts[4].'.'.substr($parts[5]0-5).PHP_EOL;
  254.             $lastCount++;
  255.         }//end foreach
  256.  
  257.     }//end explain()
  258.  
  259.  
  260.     /**
  261.      * Processes a single ruleset and returns a list of the sniffs it represents.
  262.      *
  263.      * Rules founds within the ruleset are processed immediately, but sniff classes
  264.      * are not registered by this method.
  265.      *
  266.      * @param string $rulesetPath The path to a ruleset XML file.
  267.      * @param int    $depth       How many nested processing steps we are in. This
  268.      *                             is only used for debug output.
  269.      *
  270.      * @return string[] 
  271.      * @throws RuntimeException If the ruleset path is invalid.
  272.      */
  273.     public function processRuleset($rulesetPath$depth=0)
  274.     {
  275.         $rulesetPath = Util\Common::realpath($rulesetPath);
  276.         if (PHP_CODESNIFFER_VERBOSITY > 1{
  277.             echo str_repeat("\t"$depth);
  278.             echo 'Processing ruleset '.Util\Common::stripBasepath($rulesetPath$this->config->basepath).PHP_EOL;
  279.         }
  280.  
  281.         $ruleset simplexml_load_file($rulesetPath);
  282.         if ($ruleset === false{
  283.             throw new RuntimeException("Ruleset $rulesetPath is not valid");
  284.         }
  285.  
  286.         $ownSniffs      = array();
  287.         $includedSniffs = array();
  288.         $excludedSniffs = array();
  289.  
  290.         $rulesetDir          dirname($rulesetPath);
  291.         $this->rulesetDirs[$rulesetDir;
  292.  
  293.         $sniffDir $rulesetDir.DIRECTORY_SEPARATOR.'Sniffs';
  294.         if (is_dir($sniffDir=== true{
  295.             if (PHP_CODESNIFFER_VERBOSITY > 1{
  296.                 echo str_repeat("\t"$depth);
  297.                 echo "\tAdding sniff files from ".Util\Common::stripBasepath($sniffDir$this->config->basepath).' directory'.PHP_EOL;
  298.             }
  299.  
  300.             $ownSniffs $this->expandSniffDirectory($sniffDir$depth);
  301.         }
  302.  
  303.         // Process custom sniff config settings.
  304.         foreach ($ruleset->{'config'as $config{
  305.             if ($this->shouldProcessElement($config=== false{
  306.                 continue;
  307.             }
  308.  
  309.             $this->setConfigData((string) $config['name'](string) $config['value']true);
  310.             if (PHP_CODESNIFFER_VERBOSITY > 1{
  311.                 echo str_repeat("\t"$depth);
  312.                 echo "\t=> set config value ".(string) $config['name'].': '.(string) $config['value'].PHP_EOL;
  313.             }
  314.         }
  315.  
  316.         foreach ($ruleset->rule as $rule{
  317.             if (isset($rule['ref']=== false
  318.                 || $this->shouldProcessElement($rule=== false
  319.             {
  320.                 continue;
  321.             }
  322.  
  323.             if (PHP_CODESNIFFER_VERBOSITY > 1{
  324.                 echo str_repeat("\t"$depth);
  325.                 echo "\tProcessing rule \"".$rule['ref'].'"'.PHP_EOL;
  326.             }
  327.  
  328.             $expandedSniffs $this->expandRulesetReference($rule['ref']$rulesetDir$depth);
  329.             $newSniffs      array_diff($expandedSniffs$includedSniffs);
  330.             $includedSniffs array_merge($includedSniffs$expandedSniffs);
  331.  
  332.             $parts explode('.'$rule['ref']);
  333.             if (count($parts=== 4{
  334.                 $sniffCode $parts[0].'.'.$parts[1].'.'.$parts[2];
  335.                 if (isset($this->ruleset[$sniffCode]['severity']=== true
  336.                     && $this->ruleset[$sniffCode]['severity'=== 0
  337.                 {
  338.                     // This sniff code has already been turned off, but now
  339.                     // it is being explicitly included again, so turn it back on.
  340.                     $this->ruleset[(string) $rule['ref']]['severity'= 5;
  341.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  342.                         echo str_repeat("\t"$depth);
  343.                         echo "\t\t* disabling sniff exclusion for specific message code *".PHP_EOL;
  344.                         echo str_repeat("\t"$depth);
  345.                         echo "\t\t=> severity set to 5".PHP_EOL;
  346.                     }
  347.                 else if (empty($newSniffs=== false{
  348.                     // Including a sniff that hasn't been included higher up, but
  349.                     // only including a single message from it. So turn off all messages in
  350.                     // the sniff, except this one.
  351.                     $this->ruleset[$sniffCode]['severity']            = 0;
  352.                     $this->ruleset[(string) $rule['ref']]['severity'= 5;
  353.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  354.                         echo str_repeat("\t"$depth);
  355.                         echo "\t\tExcluding sniff \"".$sniffCode.'" except for "'.$parts[3].'"'.PHP_EOL;
  356.                     }
  357.                 }//end if
  358.             }//end if
  359.  
  360.             if (isset($rule->exclude=== true{
  361.                 foreach ($rule->exclude as $exclude{
  362.                     if ($this->shouldProcessElement($exclude=== false{
  363.                         continue;
  364.                     }
  365.  
  366.                     if (PHP_CODESNIFFER_VERBOSITY > 1{
  367.                         echo str_repeat("\t"$depth);
  368.                         echo "\t\tExcluding rule \"".$exclude['name'].'"'.PHP_EOL;
  369.                     }
  370.  
  371.                     // Check if a single code is being excluded, which is a shortcut
  372.                     // for setting the severity of the message to 0.
  373.                     $parts explode('.'$exclude['name']);
  374.                     if (count($parts=== 4{
  375.                         $this->ruleset[(string) $exclude['name']]['severity'= 0;
  376.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  377.                             echo str_repeat("\t"$depth);
  378.                             echo "\t\t=> severity set to 0".PHP_EOL;
  379.                         }
  380.                     else {
  381.                         $excludedSniffs array_merge(
  382.                             $excludedSniffs,
  383.                             $this->expandRulesetReference($exclude['name']$rulesetDir($depth + 1))
  384.                         );
  385.                     }
  386.                 }//end foreach
  387.             }//end if
  388.  
  389.             $this->processRule($rule$newSniffs$depth);
  390.         }//end foreach
  391.  
  392.         // Process custom command line arguments.
  393.         $cliArgs = array();
  394.         foreach ($ruleset->{'arg'as $arg{
  395.             if ($this->shouldProcessElement($arg=== false{
  396.                 continue;
  397.             }
  398.  
  399.             if (isset($arg['name']=== true{
  400.                 $argString '--'.(string) $arg['name'];
  401.                 if (isset($arg['value']=== true{
  402.                     $argString .= '='.(string) $arg['value'];
  403.                 }
  404.             else {
  405.                 $argString '-'.(string) $arg['value'];
  406.             }
  407.  
  408.             $cliArgs[$argString;
  409.  
  410.             if (PHP_CODESNIFFER_VERBOSITY > 1{
  411.                 echo str_repeat("\t"$depth);
  412.                 echo "\t=> set command line value $argString".PHP_EOL;
  413.             }
  414.         }//end foreach
  415.  
  416.         // Set custom php ini values as CLI args.
  417.         foreach ($ruleset->{'ini'as $arg{
  418.             if ($this->shouldProcessElement($arg=== false{
  419.                 continue;
  420.             }
  421.  
  422.             if (isset($arg['name']=== false{
  423.                 continue;
  424.             }
  425.  
  426.             $name      = (string) $arg['name'];
  427.             $argString $name;
  428.             if (isset($arg['value']=== true{
  429.                 $value      = (string) $arg['value'];
  430.                 $argString .= "=$value";
  431.             else {
  432.                 $value 'true';
  433.             }
  434.  
  435.             $cliArgs['-d';
  436.             $cliArgs[$argString;
  437.  
  438.             if (PHP_CODESNIFFER_VERBOSITY > 1{
  439.                 echo str_repeat("\t"$depth);
  440.                 echo "\t=> set PHP ini value $name to $value".PHP_EOL;
  441.             }
  442.         }//end foreach
  443.  
  444.         if (empty($this->config->files=== true{
  445.             // Process hard-coded file paths.
  446.             foreach ($ruleset->{'file'as $file{
  447.                 $file      = (string) $file;
  448.                 $cliArgs[$file;
  449.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  450.                     echo str_repeat("\t"$depth);
  451.                     echo "\t=> added \"$file\" to the file list".PHP_EOL;
  452.                 }
  453.             }
  454.         }
  455.  
  456.         if (empty($cliArgs=== false{
  457.             // Change the directory so all relative paths are worked
  458.             // out based on the location of the ruleset instead of
  459.             // the location of the user.
  460.             $inPhar = Util\Common::isPharFile($rulesetDir);
  461.             if ($inPhar === false{
  462.                 $currentDir getcwd();
  463.                 chdir($rulesetDir);
  464.             }
  465.  
  466.             $this->config->setCommandLineValues($cliArgs);
  467.  
  468.             if ($inPhar === false{
  469.                 chdir($currentDir);
  470.             }
  471.         }
  472.  
  473.         // Process custom ignore pattern rules.
  474.         foreach ($ruleset->{'exclude-pattern'as $pattern{
  475.             if ($this->shouldProcessElement($pattern=== false{
  476.                 continue;
  477.             }
  478.  
  479.             if (isset($pattern['type']=== false{
  480.                 $pattern['type''absolute';
  481.             }
  482.  
  483.             $this->ignorePatterns[(string) $pattern= (string) $pattern['type'];
  484.             if (PHP_CODESNIFFER_VERBOSITY > 1{
  485.                 echo str_repeat("\t"$depth);
  486.                 echo "\t=> added global ".(string) $pattern['type'].' ignore pattern: '.(string) $pattern.PHP_EOL;
  487.             }
  488.         }
  489.  
  490.         $includedSniffs array_unique(array_merge($ownSniffs$includedSniffs));
  491.         $excludedSniffs array_unique($excludedSniffs);
  492.  
  493.         if (PHP_CODESNIFFER_VERBOSITY > 1{
  494.             $included count($includedSniffs);
  495.             $excluded count($excludedSniffs);
  496.             echo str_repeat("\t"$depth);
  497.             echo "=> Ruleset processing complete; included $included sniffs and excluded $excluded".PHP_EOL;
  498.         }
  499.  
  500.         // Merge our own sniff list with our externally included
  501.         // sniff list, but filter out any excluded sniffs.
  502.         $files = array();
  503.         foreach ($includedSniffs as $sniff{
  504.             if (in_array($sniff$excludedSniffs=== true{
  505.                 continue;
  506.             else {
  507.                 $files[= Util\Common::realpath($sniff);
  508.             }
  509.         }
  510.  
  511.         return $files;
  512.  
  513.     }//end processRuleset()
  514.  
  515.  
  516.     /**
  517.      * Expands a directory into a list of sniff files within.
  518.      *
  519.      * @param string $directory The path to a directory.
  520.      * @param int    $depth     How many nested processing steps we are in. This
  521.      *                           is only used for debug output.
  522.      *
  523.      * @return array 
  524.      */
  525.     private function expandSniffDirectory($directory$depth=0)
  526.     {
  527.         $sniffs = array();
  528.  
  529.         $rdi = new \RecursiveDirectoryIterator($directory\RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
  530.         $di  = new \RecursiveIteratorIterator($rdi0\RecursiveIteratorIterator::CATCH_GET_CHILD);
  531.  
  532.         $dirLen strlen($directory);
  533.  
  534.         foreach ($di as $file{
  535.             $filename $file->getFilename();
  536.  
  537.             // Skip hidden files.
  538.             if (substr($filename01=== '.'{
  539.                 continue;
  540.             }
  541.  
  542.             // We are only interested in PHP and sniff files.
  543.             $fileParts explode('.'$filename);
  544.             if (array_pop($fileParts!== 'php'{
  545.                 continue;
  546.             }
  547.  
  548.             $basename basename($filename'.php');
  549.             if (substr($basename-5!== 'Sniff'{
  550.                 continue;
  551.             }
  552.  
  553.             $path $file->getPathname();
  554.  
  555.             // Skip files in hidden directories within the Sniffs directory of this
  556.             // standard. We use the offset with strpos() to allow hidden directories
  557.             // before, valid example:
  558.             // /home/foo/.composer/vendor/squiz/custom_tool/MyStandard/Sniffs/...
  559.             if (strpos($pathDIRECTORY_SEPARATOR.'.'$dirLen!== false{
  560.                 continue;
  561.             }
  562.  
  563.             if (PHP_CODESNIFFER_VERBOSITY > 1{
  564.                 echo str_repeat("\t"$depth);
  565.                 echo "\t\t=> ".Util\Common::stripBasepath($path$this->config->basepath).PHP_EOL;
  566.             }
  567.  
  568.             $sniffs[$path;
  569.         }//end foreach
  570.  
  571.         return $sniffs;
  572.  
  573.     }//end expandSniffDirectory()
  574.  
  575.  
  576.     /**
  577.      * Expands a ruleset reference into a list of sniff files.
  578.      *
  579.      * @param string $ref        The reference from the ruleset XML file.
  580.      * @param string $rulesetDir The directory of the ruleset XML file, used to
  581.      *                            evaluate relative paths.
  582.      * @param int    $depth      How many nested processing steps we are in. This
  583.      *                            is only used for debug output.
  584.      *
  585.      * @return array 
  586.      * @throws RuntimeException If the reference is invalid.
  587.      */
  588.     private function expandRulesetReference($ref$rulesetDir$depth=0)
  589.     {
  590.         // Ignore internal sniffs codes as they are used to only
  591.         // hide and change internal messages.
  592.         if (substr($ref09=== 'Internal.'{
  593.             if (PHP_CODESNIFFER_VERBOSITY > 1{
  594.                 echo str_repeat("\t"$depth);
  595.                 echo "\t\t* ignoring internal sniff code *".PHP_EOL;
  596.             }
  597.  
  598.             return array();
  599.         }
  600.  
  601.         // As sniffs can't begin with a full stop, assume references in
  602.         // this format are relative paths and attempt to convert them
  603.         // to absolute paths. If this fails, let the reference run through
  604.         // the normal checks and have it fail as normal.
  605.         if (substr($ref01=== '.'{
  606.             $realpath = Util\Common::realpath($rulesetDir.'/'.$ref);
  607.             if ($realpath !== false{
  608.                 $ref $realpath;
  609.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  610.                     echo str_repeat("\t"$depth);
  611.                     echo "\t\t=> ".Util\Common::stripBasepath($ref$this->config->basepath).PHP_EOL;
  612.                 }
  613.             }
  614.         }
  615.  
  616.         // As sniffs can't begin with a tilde, assume references in
  617.         // this format are relative to the user's home directory.
  618.         if (substr($ref02=== '~/'{
  619.             $realpath = Util\Common::realpath($ref);
  620.             if ($realpath !== false{
  621.                 $ref $realpath;
  622.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  623.                     echo str_repeat("\t"$depth);
  624.                     echo "\t\t=> ".Util\Common::stripBasepath($ref$this->config->basepath).PHP_EOL;
  625.                 }
  626.             }
  627.         }
  628.  
  629.         if (is_file($ref=== true{
  630.             if (substr($ref-9=== 'Sniff.php'{
  631.                 // A single external sniff.
  632.                 $this->rulesetDirs[dirname(dirname(dirname($ref)));
  633.                 return array($ref);
  634.             }
  635.         else {
  636.             // See if this is a whole standard being referenced.
  637.             $path = Util\Standards::getInstalledStandardPath($ref);
  638.             if (Util\Common::isPharFile($path=== true && strpos($path'ruleset.xml'=== false{
  639.                 // If the ruleset exists inside the phar file, use it.
  640.                 if (file_exists($path.DIRECTORY_SEPARATOR.'ruleset.xml'=== true{
  641.                     $path $path.DIRECTORY_SEPARATOR.'ruleset.xml';
  642.                 else {
  643.                     $path = null;
  644.                 }
  645.             }
  646.  
  647.             if ($path !== null{
  648.                 $ref $path;
  649.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  650.                     echo str_repeat("\t"$depth);
  651.                     echo "\t\t=> ".Util\Common::stripBasepath($ref$this->config->basepath).PHP_EOL;
  652.                 }
  653.             else if (is_dir($ref=== false{
  654.                 // Work out the sniff path.
  655.                 $sepPos strpos($refDIRECTORY_SEPARATOR);
  656.                 if ($sepPos !== false{
  657.                     $stdName substr($ref0$sepPos);
  658.                     $path    substr($ref$sepPos);
  659.                 else {
  660.                     $parts   explode('.'$ref);
  661.                     $stdName $parts[0];
  662.                     if (count($parts=== 1{
  663.                         // A whole standard?
  664.                         $path '';
  665.                     else if (count($parts=== 2{
  666.                         // A directory of sniffs?
  667.                         $path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1];
  668.                     else {
  669.                         // A single sniff?
  670.                         $path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1].DIRECTORY_SEPARATOR.$parts[2].'Sniff.php';
  671.                     }
  672.                 }
  673.  
  674.                 $newRef  = false;
  675.                 $stdPath = Util\Standards::getInstalledStandardPath($stdName);
  676.                 if ($stdPath !== null && $path !== ''{
  677.                     if (Util\Common::isPharFile($stdPath=== true
  678.                         && strpos($stdPath'ruleset.xml'=== false
  679.                     {
  680.                         // Phar files can only return the directory,
  681.                         // since ruleset can be omitted if building one standard.
  682.                         $newRef = Util\Common::realpath($stdPath.$path);
  683.                     else {
  684.                         $newRef = Util\Common::realpath(dirname($stdPath).$path);
  685.                     }
  686.                 }
  687.  
  688.                 if ($newRef === false{
  689.                     // The sniff is not locally installed, so check if it is being
  690.                     // referenced as a remote sniff outside the install. We do this
  691.                     // by looking through all directories where we have found ruleset
  692.                     // files before, looking for ones for this particular standard,
  693.                     // and seeing if it is in there.
  694.                     foreach ($this->rulesetDirs as $dir{
  695.                         if (strtolower(basename($dir)) !== strtolower($stdName)) {
  696.                             continue;
  697.                         }
  698.  
  699.                         $newRef = Util\Common::realpath($dir.$path);
  700.  
  701.                         if ($newRef !== false{
  702.                             $ref $newRef;
  703.                         }
  704.                     }
  705.                 else {
  706.                     $ref $newRef;
  707.                 }
  708.  
  709.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  710.                     echo str_repeat("\t"$depth);
  711.                     echo "\t\t=> ".Util\Common::stripBasepath($ref$this->config->basepath).PHP_EOL;
  712.                 }
  713.             }//end if
  714.         }//end if
  715.  
  716.         if (is_dir($ref=== true{
  717.             if (is_file($ref.DIRECTORY_SEPARATOR.'ruleset.xml'=== true{
  718.                 // We are referencing an external coding standard.
  719.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  720.                     echo str_repeat("\t"$depth);
  721.                     echo "\t\t* rule is referencing a standard using directory name; processing *".PHP_EOL;
  722.                 }
  723.  
  724.                 return $this->processRuleset($ref.DIRECTORY_SEPARATOR.'ruleset.xml'($depth + 2));
  725.             else {
  726.                 // We are referencing a whole directory of sniffs.
  727.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  728.                     echo str_repeat("\t"$depth);
  729.                     echo "\t\t* rule is referencing a directory of sniffs *".PHP_EOL;
  730.                     echo str_repeat("\t"$depth);
  731.                     echo "\t\tAdding sniff files from directory".PHP_EOL;
  732.                 }
  733.  
  734.                 return $this->expandSniffDirectory($ref($depth + 1));
  735.             }
  736.         else {
  737.             if (is_file($ref=== false{
  738.                 $error = "Referenced sniff \"$ref\" does not exist";
  739.                 throw new RuntimeException($error);
  740.             }
  741.  
  742.             if (substr($ref-9=== 'Sniff.php'{
  743.                 // A single sniff.
  744.                 return array($ref);
  745.             else {
  746.                 // Assume an external ruleset.xml file.
  747.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  748.                     echo str_repeat("\t"$depth);
  749.                     echo "\t\t* rule is referencing a standard using ruleset path; processing *".PHP_EOL;
  750.                 }
  751.  
  752.                 return $this->processRuleset($ref($depth + 2));
  753.             }
  754.         }//end if
  755.  
  756.     }//end expandRulesetReference()
  757.  
  758.  
  759.     /**
  760.      * Processes a rule from a ruleset XML file, overriding built-in defaults.
  761.      *
  762.      * @param SimpleXMLElement $rule      The rule object from a ruleset XML file.
  763.      * @param string[]         $newSniffs An array of sniffs that got included by this rule.
  764.      * @param int              $depth     How many nested processing steps we are in.
  765.      *                                     This is only used for debug output.
  766.      *
  767.      * @return void 
  768.      * @throws RuntimeException If rule settings are invalid.
  769.      */
  770.     private function processRule($rule$newSniffs$depth=0)
  771.     {
  772.         $ref  = (string) $rule['ref'];
  773.         $todo = array($ref);
  774.  
  775.         $parts explode('.'$ref);
  776.         if (count($parts<= 2{
  777.             // We are processing a standard or a category of sniffs.
  778.             foreach ($newSniffs as $sniffFile{
  779.                 $parts         explode(DIRECTORY_SEPARATOR$sniffFile);
  780.                 $sniffName     array_pop($parts);
  781.                 $sniffCategory array_pop($parts);
  782.                 array_pop($parts);
  783.                 $sniffStandard array_pop($parts);
  784.                 $todo[]        $sniffStandard.'.'.$sniffCategory.'.'.substr($sniffName0-9);
  785.             }
  786.         }
  787.  
  788.         foreach ($todo as $code{
  789.             // Custom severity.
  790.             if (isset($rule->severity=== true
  791.                 && $this->shouldProcessElement($rule->severity=== true
  792.             {
  793.                 if (isset($this->ruleset[$code]=== false{
  794.                     $this->ruleset[$code= array();
  795.                 }
  796.  
  797.                 $this->ruleset[$code]['severity'= (int) $rule->severity;
  798.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  799.                     echo str_repeat("\t"$depth);
  800.                     echo "\t\t=> severity set to ".(int) $rule->severity;
  801.                     if ($code !== $ref{
  802.                         echo " for $code";
  803.                     }
  804.  
  805.                     echo PHP_EOL;
  806.                 }
  807.             }
  808.  
  809.             // Custom message type.
  810.             if (isset($rule->type=== true
  811.                 && $this->shouldProcessElement($rule->type=== true
  812.             {
  813.                 if (isset($this->ruleset[$code]=== false{
  814.                     $this->ruleset[$code= array();
  815.                 }
  816.  
  817.                 $type strtolower((string) $rule->type);
  818.                 if ($type !== 'error' && $type !== 'warning'{
  819.                     throw new RuntimeException("Message type \"$type\" is invalid; must be \"error\" or \"warning\"");
  820.                 }
  821.  
  822.                 $this->ruleset[$code]['type'$type;
  823.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  824.                     echo str_repeat("\t"$depth);
  825.                     echo "\t\t=> message type set to ".(string) $rule->type;
  826.                     if ($code !== $ref{
  827.                         echo " for $code";
  828.                     }
  829.  
  830.                     echo PHP_EOL;
  831.                 }
  832.             }//end if
  833.  
  834.             // Custom message.
  835.             if (isset($rule->message=== true
  836.                 && $this->shouldProcessElement($rule->message=== true
  837.             {
  838.                 if (isset($this->ruleset[$code]=== false{
  839.                     $this->ruleset[$code= array();
  840.                 }
  841.  
  842.                 $this->ruleset[$code]['message'= (string) $rule->message;
  843.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  844.                     echo str_repeat("\t"$depth);
  845.                     echo "\t\t=> message set to ".(string) $rule->message;
  846.                     if ($code !== $ref{
  847.                         echo " for $code";
  848.                     }
  849.  
  850.                     echo PHP_EOL;
  851.                 }
  852.             }
  853.  
  854.             // Custom properties.
  855.             if (isset($rule->properties=== true
  856.                 && $this->shouldProcessElement($rule->properties=== true
  857.             {
  858.                 foreach ($rule->properties->property as $prop{
  859.                     if ($this->shouldProcessElement($prop=== false{
  860.                         continue;
  861.                     }
  862.  
  863.                     if (isset($this->ruleset[$code]=== false{
  864.                         $this->ruleset[$code= array(
  865.                                                  'properties' => array(),
  866.                                                 );
  867.                     else if (isset($this->ruleset[$code]['properties']=== false{
  868.                         $this->ruleset[$code]['properties'= array();
  869.                     }
  870.  
  871.                     $name = (string) $prop['name'];
  872.                     if (isset($prop['type']=== true
  873.                         && (string) $prop['type'=== 'array'
  874.                     {
  875.                         $value  = (string) $prop['value'];
  876.                         $values = array();
  877.                         foreach (explode(','$valueas $val{
  878.                             $v '';
  879.  
  880.                             list($k,$vexplode('=>'$val.'=>');
  881.                             if ($v !== ''{
  882.                                 $values[$k$v;
  883.                             else {
  884.                                 $values[$k;
  885.                             }
  886.                         }
  887.  
  888.                         $this->ruleset[$code]['properties'][$name$values;
  889.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  890.                             echo str_repeat("\t"$depth);
  891.                             echo "\t\t=> array property \"$name\" set to \"$value\"";
  892.                             if ($code !== $ref{
  893.                                 echo " for $code";
  894.                             }
  895.  
  896.                             echo PHP_EOL;
  897.                         }
  898.                     else {
  899.                         $this->ruleset[$code]['properties'][$name= (string) $prop['value'];
  900.                         if (PHP_CODESNIFFER_VERBOSITY > 1{
  901.                             echo str_repeat("\t"$depth);
  902.                             echo "\t\t=> property \"$name\" set to \"".(string) $prop['value'].'"';
  903.                             if ($code !== $ref{
  904.                                 echo " for $code";
  905.                             }
  906.  
  907.                             echo PHP_EOL;
  908.                         }
  909.                     }//end if
  910.                 }//end foreach
  911.             }//end if
  912.  
  913.             // Ignore patterns.
  914.             foreach ($rule->{'exclude-pattern'as $pattern{
  915.                 if ($this->shouldProcessElement($pattern=== false{
  916.                     continue;
  917.                 }
  918.  
  919.                 if (isset($this->ignorePatterns[$code]=== false{
  920.                     $this->ignorePatterns[$code= array();
  921.                 }
  922.  
  923.                 if (isset($pattern['type']=== false{
  924.                     $pattern['type''absolute';
  925.                 }
  926.  
  927.                 $this->ignorePatterns[$code][(string) $pattern= (string) $pattern['type'];
  928.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  929.                     echo str_repeat("\t"$depth);
  930.                     echo "\t\t=> added rule-specific ".(string) $pattern['type'].' ignore pattern';
  931.                     if ($code !== $ref{
  932.                         echo " for $code";
  933.                     }
  934.  
  935.                     echo ': '.(string) $pattern.PHP_EOL;
  936.                 }
  937.             }//end foreach
  938.  
  939.             // Include patterns.
  940.             foreach ($rule->{'include-pattern'as $pattern{
  941.                 if ($this->shouldProcessElement($pattern=== false{
  942.                     continue;
  943.                 }
  944.  
  945.                 if (isset($this->includePatterns[$code]=== false{
  946.                     $this->includePatterns[$code= array();
  947.                 }
  948.  
  949.                 if (isset($pattern['type']=== false{
  950.                     $pattern['type''absolute';
  951.                 }
  952.  
  953.                 $this->includePatterns[$code][(string) $pattern= (string) $pattern['type'];
  954.                 if (PHP_CODESNIFFER_VERBOSITY > 1{
  955.                     echo str_repeat("\t"$depth);
  956.                     echo "\t\t=> added rule-specific ".(string) $pattern['type'].' include pattern';
  957.                     if ($code !== $ref{
  958.                         echo " for $code";
  959.                     }
  960.  
  961.                     echo ': '.(string) $pattern.PHP_EOL;
  962.                 }
  963.             }//end foreach
  964.         }//end foreach
  965.  
  966.     }//end processRule()
  967.  
  968.  
  969.     /**
  970.      * Determine if an element should be processed or ignored.
  971.      *
  972.      * @param SimpleXMLElement $element An object from a ruleset XML file.
  973.      *
  974.      * @return bool 
  975.      */
  976.     private function shouldProcessElement($element)
  977.     {
  978.         if (isset($element['phpcbf-only']=== false
  979.             && isset($element['phpcs-only']=== false
  980.         {
  981.             // No exceptions are being made.
  982.             return true;
  983.         }
  984.  
  985.         if (PHP_CODESNIFFER_CBF === true
  986.             && isset($element['phpcbf-only']=== true
  987.             && (string) $element['phpcbf-only'=== 'true'
  988.         {
  989.             return true;
  990.         }
  991.  
  992.         if (PHP_CODESNIFFER_CBF === false
  993.             && isset($element['phpcs-only']=== true
  994.             && (string) $element['phpcs-only'=== 'true'
  995.         {
  996.             return true;
  997.         }
  998.  
  999.         return false;
  1000.  
  1001.     }//end shouldProcessElement()
  1002.  
  1003.  
  1004.     /**
  1005.      * Loads and stores sniffs objects used for sniffing files.
  1006.      *
  1007.      * @param array $files        Paths to the sniff files to register.
  1008.      * @param array $restrictions The sniff class names to restrict the allowed
  1009.      *                             listeners to.
  1010.      * @param array $exclusions   The sniff class names to exclude from the
  1011.      *                             listeners list.
  1012.      *
  1013.      * @return void 
  1014.      */
  1015.     public function registerSniffs($files$restrictions$exclusions)
  1016.     {
  1017.         $listeners = array();
  1018.  
  1019.         foreach ($files as $file{
  1020.             // Work out where the position of /StandardName/Sniffs/... is
  1021.             // so we can determine what the class will be called.
  1022.             $sniffPos strrpos($fileDIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR);
  1023.             if ($sniffPos === false{
  1024.                 continue;
  1025.             }
  1026.  
  1027.             $slashPos strrpos(substr($file0$sniffPos)DIRECTORY_SEPARATOR);
  1028.             if ($slashPos === false{
  1029.                 continue;
  1030.             }
  1031.  
  1032.             $className = Autoload::loadFile($file);
  1033.  
  1034.             // If they have specified a list of sniffs to restrict to, check
  1035.             // to see if this sniff is allowed.
  1036.             if (empty($restrictions=== false
  1037.                 && isset($restrictions[strtolower($className)]=== false
  1038.             {
  1039.                 continue;
  1040.             }
  1041.  
  1042.             // If they have specified a list of sniffs to exclude, check
  1043.             // to see if this sniff is allowed.
  1044.             if (empty($exclusions=== false
  1045.                 && isset($exclusions[strtolower($className)]=== true
  1046.             {
  1047.                 continue;
  1048.             }
  1049.  
  1050.             // Skip abstract classes.
  1051.             $reflection = new \ReflectionClass($className);
  1052.             if ($reflection->isAbstract(=== true{
  1053.                 continue;
  1054.             }
  1055.  
  1056.             $listeners[$className$className;
  1057.  
  1058.             if (PHP_CODESNIFFER_VERBOSITY > 2{
  1059.                 echo "Registered $className".PHP_EOL;
  1060.             }
  1061.         }//end foreach
  1062.  
  1063.         $this->sniffs = $listeners;
  1064.  
  1065.     }//end registerSniffs()
  1066.  
  1067.  
  1068.     /**
  1069.      * Populates the array of PHP_CodeSniffer_Sniff's for this file.
  1070.      *
  1071.      * @return void 
  1072.      * @throws RuntimeException If sniff registration fails.
  1073.      */
  1074.     public function populateTokenListeners()
  1075.     {
  1076.         // Construct a list of listeners indexed by token being listened for.
  1077.         $this->tokenListeners = array();
  1078.  
  1079.         foreach ($this->sniffs as $sniffClass => $sniffObject{
  1080.             $this->sniffs[$sniffClass= null;
  1081.             $this->sniffs[$sniffClass= new $sniffClass();
  1082.  
  1083.             $sniffCode = Util\Common::getSniffCode($sniffClass);
  1084.             $this->sniffCodes[$sniffCode$sniffClass;
  1085.  
  1086.             // Set custom properties.
  1087.             if (isset($this->ruleset[$sniffCode]['properties']=== true{
  1088.                 foreach ($this->ruleset[$sniffCode]['properties'as $name => $value{
  1089.                     $this->setSniffProperty($sniffClass$name$value);
  1090.                 }
  1091.             }
  1092.  
  1093.             $tokenizers = array();
  1094.             $vars       get_class_vars($sniffClass);
  1095.             if (isset($vars['supportedTokenizers']=== true{
  1096.                 foreach ($vars['supportedTokenizers'as $tokenizer{
  1097.                     $tokenizers[$tokenizer$tokenizer;
  1098.                 }
  1099.             else {
  1100.                 $tokenizers = array('PHP' => 'PHP');
  1101.             }
  1102.  
  1103.             $tokens $this->sniffs[$sniffClass]->register();
  1104.             if (is_array($tokens=== false{
  1105.                 $msg = "Sniff $sniffClass register() method must return an array";
  1106.                 throw new RuntimeException($msg);
  1107.             }
  1108.  
  1109.             $ignorePatterns = array();
  1110.             $patterns       $this->getIgnorePatterns($sniffCode);
  1111.             foreach ($patterns as $pattern => $type{
  1112.                 $replacements = array(
  1113.                                  '\\,' => ',',
  1114.                                  '*'   => '.*',
  1115.                                 );
  1116.  
  1117.                 $ignorePatterns[strtr($pattern$replacements);
  1118.             }
  1119.  
  1120.             $includePatterns = array();
  1121.             $patterns        $this->getIncludePatterns($sniffCode);
  1122.             foreach ($patterns as $pattern => $type{
  1123.                 $replacements = array(
  1124.                                  '\\,' => ',',
  1125.                                  '*'   => '.*',
  1126.                                 );
  1127.  
  1128.                 $includePatterns[strtr($pattern$replacements);
  1129.             }
  1130.  
  1131.             foreach ($tokens as $token{
  1132.                 if (isset($this->tokenListeners[$token]=== false{
  1133.                     $this->tokenListeners[$token= array();
  1134.                 }
  1135.  
  1136.                 if (isset($this->tokenListeners[$token][$sniffClass]=== false{
  1137.                     $this->tokenListeners[$token][$sniffClass= array(
  1138.                                                                   'class'      => $sniffClass,
  1139.                                                                   'source'     => $sniffCode,
  1140.                                                                   'tokenizers' => $tokenizers,
  1141.                                                                   'ignore'     => $ignorePatterns,
  1142.                                                                   'include'    => $includePatterns,
  1143.                                                                  );
  1144.                 }
  1145.             }
  1146.         }//end foreach
  1147.  
  1148.     }//end populateTokenListeners()
  1149.  
  1150.  
  1151.     /**
  1152.      * Set a single property for a sniff.
  1153.      *
  1154.      * @param string $sniffClass The class name of the sniff.
  1155.      * @param string $name       The name of the property to change.
  1156.      * @param string $value      The new value of the property.
  1157.      *
  1158.      * @return void 
  1159.      */
  1160.     public function setSniffProperty($sniffClass$name$value)
  1161.     {
  1162.         // Setting a property for a sniff we are not using.
  1163.         if (isset($this->sniffs[$sniffClass]=== false{
  1164.             return;
  1165.         }
  1166.  
  1167.         $name trim($name);
  1168.         if (is_string($value=== true{
  1169.             $value trim($value);
  1170.         }
  1171.  
  1172.         // Special case for booleans.
  1173.         if ($value === 'true'{
  1174.             $value = true;
  1175.         else if ($value === 'false'{
  1176.             $value = false;
  1177.         }
  1178.  
  1179.         $this->sniffs[$sniffClass]->$name $value;
  1180.  
  1181.     }//end setSniffProperty()
  1182.  
  1183.  
  1184.     /**
  1185.      * Gets the array of ignore patterns.
  1186.      *
  1187.      * Optionally takes a listener to get ignore patterns specified
  1188.      * for that sniff only.
  1189.      *
  1190.      * @param string $listener The listener to get patterns for. If NULL, all
  1191.      *                          patterns are returned.
  1192.      *
  1193.      * @return array 
  1194.      */
  1195.     public function getIgnorePatterns($listener=null)
  1196.     {
  1197.         if ($listener === null{
  1198.             return $this->ignorePatterns;
  1199.         }
  1200.  
  1201.         if (isset($this->ignorePatterns[$listener]=== true{
  1202.             return $this->ignorePatterns[$listener];
  1203.         }
  1204.  
  1205.         return array();
  1206.  
  1207.     }//end getIgnorePatterns()
  1208.  
  1209.  
  1210.     /**
  1211.      * Gets the array of include patterns.
  1212.      *
  1213.      * Optionally takes a listener to get include patterns specified
  1214.      * for that sniff only.
  1215.      *
  1216.      * @param string $listener The listener to get patterns for. If NULL, all
  1217.      *                          patterns are returned.
  1218.      *
  1219.      * @return array 
  1220.      */
  1221.     public function getIncludePatterns($listener=null)
  1222.     {
  1223.         if ($listener === null{
  1224.             return $this->includePatterns;
  1225.         }
  1226.  
  1227.         if (isset($this->includePatterns[$listener]=== true{
  1228.             return $this->includePatterns[$listener];
  1229.         }
  1230.  
  1231.         return array();
  1232.  
  1233.     }//end getIncludePatterns()
  1234.  
  1235.  
  1236. }//end class

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