Completed
Push — master ( da65dc...5751ed )
by Tomáš
20:43 queued 16:49
created

Ruleset::getIgnorePatterns()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 13
Ratio 100 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 13
loc 13
rs 9.4285
cc 3
eloc 6
nc 3
nop 1
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 <[email protected]>
8
 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
9
 * @license   https://github.com/squizlabs/Symplify\PHP7_CodeSniffer/blob/master/licence.txt BSD Licence
10
 */
11
12
namespace Symplify\PHP7_CodeSniffer;
13
14
use Symplify\PHP7_CodeSniffer\Util;
15
use Symplify\PHP7_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, \Symplify\PHP7_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, \Symplify\PHP7_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 \Symplify\PHP7_CodeSniffer\Config
115
     */
116
    private $config = null;
117
118
119
    /**
120
     * Initialise the ruleset that the run will use.
121
     *
122
     * @param \Symplify\PHP7_CodeSniffer\Config $config The config data for the run.
123
     *
124
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
125
     */
126
    public function __construct(Config $config)
127
    {
128
        // Ignore sniff restrictions if caching is on.
129
        $restrictions = array();
130
        if ($config->cache === false) {
0 ignored issues
show
Documentation introduced by
The property cache does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
131
            $restrictions = $config->sniffs;
0 ignored issues
show
Documentation introduced by
The property sniffs does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
132
        }
133
134
        $this->config = $config;
135
136
        $sniffs = array();
137
        foreach ($config->standards as $standard) {
0 ignored issues
show
Documentation introduced by
The property standards does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
138
            $installed = Util\Standards::getInstalledStandardPath($standard);
139
            if ($installed === null) {
140
                $standard = Util\Common::realpath($standard);
141
                if (is_dir($standard) === true
142
                    && is_file(Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml')) === true
143
                ) {
144
                    $standard = Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml');
145
                }
146
            } else {
147
                $standard = $installed;
148
            }
149
150
            $ruleset = simplexml_load_file($standard);
151
            if ($ruleset !== false) {
152
                $standardName = (string) $ruleset['name'];
153
                if ($this->name !== '') {
154
                    $this->name .= ', ';
155
                }
156
157
                $this->name   .= $standardName;
158
                $this->paths[] = $standard;
159
            }
160
161
            if (PHP_CodeSniffer_VERBOSITY === 1) {
162
                echo "Registering sniffs in the $standardName standard... ";
0 ignored issues
show
Bug introduced by
The variable $standardName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
163
                if (count($config->standards) > 1 || PHP_CodeSniffer_VERBOSITY > 2) {
0 ignored issues
show
Documentation introduced by
The property standards does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
164
                    echo PHP_EOL;
165
                }
166
            }
167
168
            $sniffs = array_merge($sniffs, $this->processRuleset($standard));
169
        }//end foreach
170
171
        $sniffRestrictions = array();
172
        foreach ($restrictions as $sniffCode) {
173
            $parts     = explode('.', strtolower($sniffCode));
174
            $sniffName = 'Symplify\PHP7_CodeSniffer\standards\\'.$parts[0].'\sniffs\\'.$parts[1].'\\'.$parts[2].'sniff';
175
            $sniffRestrictions[$sniffName] = true;
176
        }
177
178
        $this->registerSniffs($sniffs, $sniffRestrictions);
179
        $this->populateTokenListeners();
180
181
        if (PHP_CodeSniffer_VERBOSITY === 1) {
182
            $numSniffs = count($this->sniffs);
183
            echo "DONE ($numSniffs sniffs registered)".PHP_EOL;
184
        }
185
186
    }//end __construct()
187
188
189
    /**
190
     * Prints a report showing the sniffs contained in a standard.
191
     *
192
     * @return void
193
     */
194
    public function explain()
195
    {
196
        $sniffs = array_keys($this->sniffs);
197
        sort($sniffs);
198
199
        ob_start();
200
201
        $lastStandard = null;
202
        $lastCount    = '';
203
        $sniffCount   = count($sniffs);
204
205
        // Add a dummy entry to the end so we loop
206
        // one last time and clear the output buffer.
207
        $sniffs[] = '';
208
209
        echo PHP_EOL."The $this->name standard contains $sniffCount sniffs".PHP_EOL;
210
211
        ob_start();
212
213
        foreach ($sniffs as $i => $sniff) {
214
            if ($i === $sniffCount) {
215
                $currentStandard = null;
216
            } else {
217
                $parts = explode('\\', $sniff);
218
219
                $currentStandard = $parts[2];
220
                if ($lastStandard === null) {
221
                    $lastStandard = $currentStandard;
222
                }
223
            }
224
225
            if ($currentStandard !== $lastStandard) {
226
                $sniffList = ob_get_contents();
227
                ob_end_clean();
228
229
                echo PHP_EOL.$lastStandard.' ('.$lastCount.' sniffs)'.PHP_EOL;
230
                echo str_repeat('-', (strlen($lastStandard.$lastCount) + 10));
231
                echo PHP_EOL;
232
                echo $sniffList;
233
234
                $lastStandard = $parts[2];
0 ignored issues
show
Bug introduced by
The variable $parts does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
235
                $lastCount    = 0;
236
237
                if ($currentStandard === null) {
238
                    break;
239
                }
240
241
                ob_start();
242
            }
243
244
            echo '  '.$parts[2].'.'.$parts[4].'.'.substr($parts[5], 0, -5).PHP_EOL;
245
            $lastCount++;
246
        }//end foreach
247
248
    }//end explain()
249
250
251
    /**
252
     * Processes a single ruleset and returns a list of the sniffs it represents.
253
     *
254
     * Rules founds within the ruleset are processed immediately, but sniff classes
255
     * are not registered by this method.
256
     *
257
     * @param string $rulesetPath The path to a ruleset XML file.
258
     * @param int    $depth       How many nested processing steps we are in. This
259
     *                            is only used for debug output.
260
     *
261
     * @return string[]
262
     * @throws RuntimeException If the ruleset path is invalid.
263
     */
264
    public function processRuleset($rulesetPath, $depth=0)
265
    {
266
        $rulesetPath = Util\Common::realpath($rulesetPath);
267
        if (PHP_CodeSniffer_VERBOSITY > 1) {
268
            echo str_repeat("\t", $depth);
269
            echo 'Processing ruleset '.Util\Common::stripBasepath($rulesetPath, $this->config->basepath).PHP_EOL;
0 ignored issues
show
Documentation introduced by
The property basepath does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
270
        }
271
272
        $ruleset = simplexml_load_file($rulesetPath);
273
        if ($ruleset === false) {
274
            throw new RuntimeException("Ruleset $rulesetPath is not valid");
275
        }
276
277
        $ownSniffs      = array();
278
        $includedSniffs = array();
279
        $excludedSniffs = array();
280
281
        $rulesetDir          = dirname($rulesetPath);
282
        $this->rulesetDirs[] = $rulesetDir;
283
284
        $sniffDir = $rulesetDir.DIRECTORY_SEPARATOR.'Sniffs';
285
        if (is_dir($sniffDir) === true) {
286
            if (PHP_CodeSniffer_VERBOSITY > 1) {
287
                echo str_repeat("\t", $depth);
288
                echo "\tAdding sniff files from ".Util\Common::stripBasepath($sniffDir, $this->config->basepath).' directory'.PHP_EOL;
0 ignored issues
show
Documentation introduced by
The property basepath does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
289
            }
290
291
            $ownSniffs = $this->expandSniffDirectory($sniffDir, $depth);
292
        }
293
294
        // Process custom sniff config settings.
295
        foreach ($ruleset->{'config'} as $config) {
296
            if ($this->shouldProcessElement($config) === false) {
297
                continue;
298
            }
299
300
            $this->setConfigData((string) $config['name'], (string) $config['value'], true);
0 ignored issues
show
Bug introduced by
The method setConfigData() does not seem to exist on object<Symplify\PHP7_CodeSniffer\Ruleset>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
301 View Code Duplication
            if (PHP_CodeSniffer_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
302
                echo str_repeat("\t", $depth);
303
                echo "\t=> set config value ".(string) $config['name'].': '.(string) $config['value'].PHP_EOL;
304
            }
305
        }
306
307
        foreach ($ruleset->rule as $rule) {
308
            if (isset($rule['ref']) === false
309
                || $this->shouldProcessElement($rule) === false
310
            ) {
311
                continue;
312
            }
313
314 View Code Duplication
            if (PHP_CodeSniffer_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
315
                echo str_repeat("\t", $depth);
316
                echo "\tProcessing rule \"".$rule['ref'].'"'.PHP_EOL;
317
            }
318
319
            $expandedSniffs = $this->expandRulesetReference($rule['ref'], $rulesetDir, $depth);
320
            $newSniffs      = array_diff($expandedSniffs, $includedSniffs);
321
            $includedSniffs = array_merge($includedSniffs, $expandedSniffs);
322
323
            $parts = explode('.', $rule['ref']);
324
            if (count($parts) === 4) {
325
                $sniffCode = $parts[0].'.'.$parts[1].'.'.$parts[2];
326
                if (isset($this->ruleset[$sniffCode]['severity']) === true
327
                    && $this->ruleset[$sniffCode]['severity'] === 0
328
                ) {
329
                    // This sniff code has already been turned off, but now
330
                    // it is being explicitly included again, so turn it back on.
331
                    $this->ruleset[(string) $rule['ref']]['severity'] = 5;
332 View Code Duplication
                    if (PHP_CodeSniffer_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
333
                        echo str_repeat("\t", $depth);
334
                        echo "\t\t* disabling sniff exclusion for specific message code *".PHP_EOL;
335
                        echo str_repeat("\t", $depth);
336
                        echo "\t\t=> severity set to 5".PHP_EOL;
337
                    }
338
                } else if (empty($newSniffs) === false) {
339
                    // Including a sniff that hasn't been included higher up, but
340
                    // only including a single message from it. So turn off all messages in
341
                    // the sniff, except this one.
342
                    $this->ruleset[$sniffCode]['severity']            = 0;
343
                    $this->ruleset[(string) $rule['ref']]['severity'] = 5;
344 View Code Duplication
                    if (PHP_CodeSniffer_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
345
                        echo str_repeat("\t", $depth);
346
                        echo "\t\tExcluding sniff \"".$sniffCode.'" except for "'.$parts[3].'"'.PHP_EOL;
347
                    }
348
                }//end if
349
            }//end if
350
351
            if (isset($rule->exclude) === true) {
352
                foreach ($rule->exclude as $exclude) {
353
                    if ($this->shouldProcessElement($exclude) === false) {
354
                        continue;
355
                    }
356
357 View Code Duplication
                    if (PHP_CodeSniffer_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
358
                        echo str_repeat("\t", $depth);
359
                        echo "\t\tExcluding rule \"".$exclude['name'].'"'.PHP_EOL;
360
                    }
361
362
                    // Check if a single code is being excluded, which is a shortcut
363
                    // for setting the severity of the message to 0.
364
                    $parts = explode('.', $exclude['name']);
365
                    if (count($parts) === 4) {
366
                        $this->ruleset[(string) $exclude['name']]['severity'] = 0;
367
                        if (PHP_CodeSniffer_VERBOSITY > 1) {
368
                            echo str_repeat("\t", $depth);
369
                            echo "\t\t=> severity set to 0".PHP_EOL;
370
                        }
371
                    } else {
372
                        $excludedSniffs = array_merge(
373
                            $excludedSniffs,
374
                            $this->expandRulesetReference($exclude['name'], $rulesetDir, ($depth + 1))
375
                        );
376
                    }
377
                }//end foreach
378
            }//end if
379
380
            $this->processRule($rule, $newSniffs, $depth);
381
        }//end foreach
382
383
        // Process custom command line arguments.
384
        $cliArgs = array();
385
        foreach ($ruleset->{'arg'} as $arg) {
386
            if ($this->shouldProcessElement($arg) === false) {
387
                continue;
388
            }
389
390
            if (isset($arg['name']) === true) {
391
                $argString = '--'.(string) $arg['name'];
392
                if (isset($arg['value']) === true) {
393
                    $argString .= '='.(string) $arg['value'];
394
                }
395
            } else {
396
                $argString = '-'.(string) $arg['value'];
397
            }
398
399
            $cliArgs[] = $argString;
400
401
            if (PHP_CodeSniffer_VERBOSITY > 1) {
402
                echo str_repeat("\t", $depth);
403
                echo "\t=> set command line value $argString".PHP_EOL;
404
            }
405
        }//end foreach
406
407
        // Set custom php ini values as CLI args.
408
        foreach ($ruleset->{'ini'} as $arg) {
409
            if ($this->shouldProcessElement($arg) === false) {
410
                continue;
411
            }
412
413
            if (isset($arg['name']) === false) {
414
                continue;
415
            }
416
417
            $name      = (string) $arg['name'];
418
            $argString = $name;
419
            if (isset($arg['value']) === true) {
420
                $value      = (string) $arg['value'];
421
                $argString .= "=$value";
422
            } else {
423
                $value = 'true';
424
            }
425
426
            $cliArgs[] = '-d';
427
            $cliArgs[] = $argString;
428
429
            if (PHP_CodeSniffer_VERBOSITY > 1) {
430
                echo str_repeat("\t", $depth);
431
                echo "\t=> set PHP ini value $name to $value".PHP_EOL;
432
            }
433
        }//end foreach
434
435
        if (empty($this->config->files) === true) {
0 ignored issues
show
Documentation introduced by
The property files does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
436
            // Process hard-coded file paths.
437
            foreach ($ruleset->{'file'} as $file) {
438
                $file      = (string) $file;
439
                $cliArgs[] = $file;
440
                if (PHP_CodeSniffer_VERBOSITY > 1) {
441
                    echo str_repeat("\t", $depth);
442
                    echo "\t=> added \"$file\" to the file list".PHP_EOL;
443
                }
444
            }
445
        }
446
447
        if (empty($cliArgs) === false) {
448
            // Change the directory so all relative paths are worked
449
            // out based on the location of the ruleset instead of
450
            // the location of the user.
451
            $currentDir = getcwd();
452
            chdir($rulesetDir);
453
            $this->config->setCommandLineValues($cliArgs);
454
            chdir($currentDir);
455
        }
456
457
        // Process custom ignore pattern rules.
458
        foreach ($ruleset->{'exclude-pattern'} as $pattern) {
459
            if ($this->shouldProcessElement($pattern) === false) {
460
                continue;
461
            }
462
463
            if (isset($pattern['type']) === false) {
464
                $pattern['type'] = 'absolute';
465
            }
466
467
            $this->ignorePatterns[(string) $pattern] = (string) $pattern['type'];
468 View Code Duplication
            if (PHP_CodeSniffer_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
469
                echo str_repeat("\t", $depth);
470
                echo "\t=> added global ".(string) $pattern['type'].' ignore pattern: '.(string) $pattern.PHP_EOL;
471
            }
472
        }
473
474
        $includedSniffs = array_unique(array_merge($ownSniffs, $includedSniffs));
475
        $excludedSniffs = array_unique($excludedSniffs);
476
477
        if (PHP_CodeSniffer_VERBOSITY > 1) {
478
            $included = count($includedSniffs);
479
            $excluded = count($excludedSniffs);
480
            echo str_repeat("\t", $depth);
481
            echo "=> Ruleset processing complete; included $included sniffs and excluded $excluded".PHP_EOL;
482
        }
483
484
        // Merge our own sniff list with our externally included
485
        // sniff list, but filter out any excluded sniffs.
486
        $files = array();
487
        foreach ($includedSniffs as $sniff) {
488
            if (in_array($sniff, $excludedSniffs) === true) {
489
                continue;
490
            } else {
491
                $files[] = Util\Common::realpath($sniff);
492
            }
493
        }
494
495
        return $files;
496
497
    }//end processRuleset()
498
499
500
    /**
501
     * Expands a directory into a list of sniff files within.
502
     *
503
     * @param string $directory The path to a directory.
504
     * @param int    $depth     How many nested processing steps we are in. This
505
     *                          is only used for debug output.
506
     *
507
     * @return array
508
     */
509
    private function expandSniffDirectory($directory, $depth=0)
510
    {
511
        $sniffs = array();
512
513
        $rdi = new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
514
        $di  = new \RecursiveIteratorIterator($rdi, 0, \RecursiveIteratorIterator::CATCH_GET_CHILD);
515
516
        $dirLen = strlen($directory);
517
518
        foreach ($di as $file) {
519
            $filename = $file->getFilename();
520
521
            // Skip hidden files.
522
            if (substr($filename, 0, 1) === '.') {
523
                continue;
524
            }
525
526
            // We are only interested in PHP and sniff files.
527
            $fileParts = explode('.', $filename);
528
            if (array_pop($fileParts) !== 'php') {
529
                continue;
530
            }
531
532
            $basename = basename($filename, '.php');
533
            if (substr($basename, -5) !== 'Sniff') {
534
                continue;
535
            }
536
537
            $path = $file->getPathname();
538
539
            // Skip files in hidden directories within the Sniffs directory of this
540
            // standard. We use the offset with strpos() to allow hidden directories
541
            // before, valid example:
542
            // /home/foo/.composer/vendor/squiz/custom_tool/MyStandard/Sniffs/...
543
            if (strpos($path, DIRECTORY_SEPARATOR.'.', $dirLen) !== false) {
544
                continue;
545
            }
546
547
            if (PHP_CodeSniffer_VERBOSITY > 1) {
548
                echo str_repeat("\t", $depth);
549
                echo "\t\t=> ".Util\Common::stripBasepath($path, $this->config->basepath).PHP_EOL;
0 ignored issues
show
Documentation introduced by
The property basepath does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
550
            }
551
552
            $sniffs[] = $path;
553
        }//end foreach
554
555
        return $sniffs;
556
557
    }//end expandSniffDirectory()
558
559
560
    /**
561
     * Expands a ruleset reference into a list of sniff files.
562
     *
563
     * @param string $ref        The reference from the ruleset XML file.
564
     * @param string $rulesetDir The directory of the ruleset XML file, used to
565
     *                           evaluate relative paths.
566
     * @param int    $depth      How many nested processing steps we are in. This
567
     *                           is only used for debug output.
568
     *
569
     * @return array
570
     * @throws RuntimeException If the reference is invalid.
571
     */
572
    private function expandRulesetReference($ref, $rulesetDir, $depth=0)
573
    {
574
        // Ignore internal sniffs codes as they are used to only
575
        // hide and change internal messages.
576 View Code Duplication
        if (substr($ref, 0, 9) === 'Internal.') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
577
            if (PHP_CodeSniffer_VERBOSITY > 1) {
578
                echo str_repeat("\t", $depth);
579
                echo "\t\t* ignoring internal sniff code *".PHP_EOL;
580
            }
581
582
            return array();
583
        }
584
585
        // As sniffs can't begin with a full stop, assume references in
586
        // this format are relative paths and attempt to convert them
587
        // to absolute paths. If this fails, let the reference run through
588
        // the normal checks and have it fail as normal.
589 View Code Duplication
        if (substr($ref, 0, 1) === '.') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
590
            $realpath = Util\Common::realpath($rulesetDir.'/'.$ref);
591
            if ($realpath !== false) {
592
                $ref = $realpath;
593
                if (PHP_CodeSniffer_VERBOSITY > 1) {
594
                    echo str_repeat("\t", $depth);
595
                    echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
0 ignored issues
show
Documentation introduced by
The property basepath does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
596
                }
597
            }
598
        }
599
600
        // As sniffs can't begin with a tilde, assume references in
601
        // this format are relative to the user's home directory.
602 View Code Duplication
        if (substr($ref, 0, 2) === '~/') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
603
            $realpath = Util\Common::realpath($ref);
604
            if ($realpath !== false) {
605
                $ref = $realpath;
606
                if (PHP_CodeSniffer_VERBOSITY > 1) {
607
                    echo str_repeat("\t", $depth);
608
                    echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
0 ignored issues
show
Documentation introduced by
The property basepath does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
609
                }
610
            }
611
        }
612
613
        if (is_file($ref) === true) {
614
            if (substr($ref, -9) === 'Sniff.php') {
615
                // A single external sniff.
616
                $this->rulesetDirs[] = dirname(dirname(dirname($ref)));
617
                return array($ref);
618
            }
619
        } else {
620
            // See if this is a whole standard being referenced.
621
            $path = Util\Standards::getInstalledStandardPath($ref);
622
623
            if ($path !== null) {
624
                $ref = $path;
625
                if (PHP_CodeSniffer_VERBOSITY > 1) {
626
                    echo str_repeat("\t", $depth);
627
                    echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
0 ignored issues
show
Documentation introduced by
The property basepath does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
628
                }
629
            } else if (is_dir($ref) === false) {
630
                // Work out the sniff path.
631
                $sepPos = strpos($ref, DIRECTORY_SEPARATOR);
632
                if ($sepPos !== false) {
633
                    $stdName = substr($ref, 0, $sepPos);
634
                    $path    = substr($ref, $sepPos);
635
                } else {
636
                    $parts   = explode('.', $ref);
637
                    $stdName = $parts[0];
638
                    if (count($parts) === 1) {
639
                        // A whole standard?
640
                        $path = '';
641
                    } else if (count($parts) === 2) {
642
                        // A directory of sniffs?
643
                        $path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1];
644
                    } else {
645
                        // A single sniff?
646
                        $path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1].DIRECTORY_SEPARATOR.$parts[2].'Sniff.php';
647
                    }
648
                }
649
650
                $newRef  = false;
651
                $stdPath = Util\Standards::getInstalledStandardPath($stdName);
652
                if ($stdPath !== null && $path !== '') {
653
                    $newRef = Util\Common::realpath(dirname($stdPath).$path);
654
                }
655
656
                if ($newRef === false) {
657
                    // The sniff is not locally installed, so check if it is being
658
                    // referenced as a remote sniff outside the install. We do this
659
                    // by looking through all directories where we have found ruleset
660
                    // files before, looking for ones for this particular standard,
661
                    // and seeing if it is in there.
662
                    foreach ($this->rulesetDirs as $dir) {
663
                        if (strtolower(basename($dir)) !== strtolower($stdName)) {
664
                            continue;
665
                        }
666
667
                        $newRef = Util\Common::realpath($dir.$path);
668
669
                        if ($newRef !== false) {
670
                            $ref = $newRef;
671
                        }
672
                    }
673
                } else {
674
                    $ref = $newRef;
675
                }
676
677
                if (PHP_CodeSniffer_VERBOSITY > 1) {
678
                    echo str_repeat("\t", $depth);
679
                    echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
0 ignored issues
show
Documentation introduced by
The property basepath does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
680
                }
681
            }//end if
682
        }//end if
683
684
        if (is_dir($ref) === true) {
685
            if (is_file($ref.DIRECTORY_SEPARATOR.'ruleset.xml') === true) {
686
                // We are referencing an external coding standard.
687
                if (PHP_CodeSniffer_VERBOSITY > 1) {
688
                    echo str_repeat("\t", $depth);
689
                    echo "\t\t* rule is referencing a standard using directory name; processing *".PHP_EOL;
690
                }
691
692
                return $this->processRuleset($ref.DIRECTORY_SEPARATOR.'ruleset.xml', ($depth + 2));
693
            } else {
694
                // We are referencing a whole directory of sniffs.
695 View Code Duplication
                if (PHP_CodeSniffer_VERBOSITY > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
696
                    echo str_repeat("\t", $depth);
697
                    echo "\t\t* rule is referencing a directory of sniffs *".PHP_EOL;
698
                    echo str_repeat("\t", $depth);
699
                    echo "\t\tAdding sniff files from directory".PHP_EOL;
700
                }
701
702
                return $this->expandSniffDirectory($ref, ($depth + 1));
703
            }
704
        } else {
705
            if (is_file($ref) === false) {
706
                $error = "Referenced sniff \"$ref\" does not exist";
707
                throw new RuntimeException($error);
708
            }
709
710
            if (substr($ref, -9) === 'Sniff.php') {
711
                // A single sniff.
712
                return array($ref);
713
            } else {
714
                // Assume an external ruleset.xml file.
715
                if (PHP_CodeSniffer_VERBOSITY > 1) {
716
                    echo str_repeat("\t", $depth);
717
                    echo "\t\t* rule is referencing a standard using ruleset path; processing *".PHP_EOL;
718
                }
719
720
                return $this->processRuleset($ref, ($depth + 2));
721
            }
722
        }//end if
723
724
    }//end expandRulesetReference()
725
726
727
    /**
728
     * Processes a rule from a ruleset XML file, overriding built-in defaults.
729
     *
730
     * @param SimpleXMLElement $rule      The rule object from a ruleset XML file.
731
     * @param string[]         $newSniffs An array of sniffs that got included by this rule.
732
     * @param int              $depth     How many nested processing steps we are in.
733
     *                                    This is only used for debug output.
734
     *
735
     * @return void
736
     * @throws RuntimeException If rule settings are invalid.
737
     */
738
    private function processRule($rule, $newSniffs, $depth=0)
739
    {
740
        $ref  = (string) $rule['ref'];
741
        $todo = array($ref);
742
743
        $parts = explode('.', $ref);
744
        if (count($parts) <= 2) {
745
            // We are processing a standard or a category of sniffs.
746
            foreach ($newSniffs as $sniffFile) {
747
                $parts         = explode(DIRECTORY_SEPARATOR, $sniffFile);
748
                $sniffName     = array_pop($parts);
749
                $sniffCategory = array_pop($parts);
750
                array_pop($parts);
751
                $sniffStandard = array_pop($parts);
752
                $todo[]        = $sniffStandard.'.'.$sniffCategory.'.'.substr($sniffName, 0, -9);
753
            }
754
        }
755
756
        foreach ($todo as $code) {
757
            // Custom severity.
758 View Code Duplication
            if (isset($rule->severity) === true
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
759
                && $this->shouldProcessElement($rule->severity) === true
760
            ) {
761
                if (isset($this->ruleset[$code]) === false) {
762
                    $this->ruleset[$code] = array();
763
                }
764
765
                $this->ruleset[$code]['severity'] = (int) $rule->severity;
766
                if (PHP_CodeSniffer_VERBOSITY > 1) {
767
                    echo str_repeat("\t", $depth);
768
                    echo "\t\t=> severity set to ".(int) $rule->severity;
769
                    if ($code !== $ref) {
770
                        echo " for $code";
771
                    }
772
773
                    echo PHP_EOL;
774
                }
775
            }
776
777
            // Custom message type.
778
            if (isset($rule->type) === true
779
                && $this->shouldProcessElement($rule->type) === true
780
            ) {
781
                if (isset($this->ruleset[$code]) === false) {
782
                    $this->ruleset[$code] = array();
783
                }
784
785
                $type = strtolower((string) $rule->type);
786
                if ($type !== 'error' && $type !== 'warning') {
787
                    throw new RuntimeException("Message type \"$type\" is invalid; must be \"error\" or \"warning\"");
788
                }
789
790
                $this->ruleset[$code]['type'] = $type;
791
                if (PHP_CodeSniffer_VERBOSITY > 1) {
792
                    echo str_repeat("\t", $depth);
793
                    echo "\t\t=> message type set to ".(string) $rule->type;
794
                    if ($code !== $ref) {
795
                        echo " for $code";
796
                    }
797
798
                    echo PHP_EOL;
799
                }
800
            }//end if
801
802
            // Custom message.
803 View Code Duplication
            if (isset($rule->message) === true
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
804
                && $this->shouldProcessElement($rule->message) === true
805
            ) {
806
                if (isset($this->ruleset[$code]) === false) {
807
                    $this->ruleset[$code] = array();
808
                }
809
810
                $this->ruleset[$code]['message'] = (string) $rule->message;
811
                if (PHP_CodeSniffer_VERBOSITY > 1) {
812
                    echo str_repeat("\t", $depth);
813
                    echo "\t\t=> message set to ".(string) $rule->message;
814
                    if ($code !== $ref) {
815
                        echo " for $code";
816
                    }
817
818
                    echo PHP_EOL;
819
                }
820
            }
821
822
            // Custom properties.
823
            if (isset($rule->properties) === true
824
                && $this->shouldProcessElement($rule->properties) === true
825
            ) {
826
                foreach ($rule->properties->property as $prop) {
827
                    if ($this->shouldProcessElement($prop) === false) {
828
                        continue;
829
                    }
830
831
                    if (isset($this->ruleset[$code]) === false) {
832
                        $this->ruleset[$code] = array(
833
                                                 'properties' => array(),
834
                                                );
835
                    } else if (isset($this->ruleset[$code]['properties']) === false) {
836
                        $this->ruleset[$code]['properties'] = array();
837
                    }
838
839
                    $name = (string) $prop['name'];
840
                    if (isset($prop['type']) === true
841
                        && (string) $prop['type'] === 'array'
842
                    ) {
843
                        $value  = (string) $prop['value'];
844
                        $values = array();
845
                        foreach (explode(',', $value) as $val) {
846
                            $v = '';
847
848
                            list($k,$v) = explode('=>', $val.'=>');
849
                            if ($v !== '') {
850
                                $values[$k] = $v;
851
                            } else {
852
                                $values[] = $k;
853
                            }
854
                        }
855
856
                        $this->ruleset[$code]['properties'][$name] = $values;
857
                        if (PHP_CodeSniffer_VERBOSITY > 1) {
858
                            echo str_repeat("\t", $depth);
859
                            echo "\t\t=> array property \"$name\" set to \"$value\"";
860
                            if ($code !== $ref) {
861
                                echo " for $code";
862
                            }
863
864
                            echo PHP_EOL;
865
                        }
866
                    } else {
867
                        $this->ruleset[$code]['properties'][$name] = (string) $prop['value'];
868
                        if (PHP_CodeSniffer_VERBOSITY > 1) {
869
                            echo str_repeat("\t", $depth);
870
                            echo "\t\t=> property \"$name\" set to \"".(string) $prop['value'].'"';
871
                            if ($code !== $ref) {
872
                                echo " for $code";
873
                            }
874
875
                            echo PHP_EOL;
876
                        }
877
                    }//end if
878
                }//end foreach
879
            }//end if
880
881
            // Ignore patterns.
882 View Code Duplication
            foreach ($rule->{'exclude-pattern'} as $pattern) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
883
                if ($this->shouldProcessElement($pattern) === false) {
884
                    continue;
885
                }
886
887
                if (isset($this->ignorePatterns[$code]) === false) {
888
                    $this->ignorePatterns[$code] = array();
889
                }
890
891
                if (isset($pattern['type']) === false) {
892
                    $pattern['type'] = 'absolute';
893
                }
894
895
                $this->ignorePatterns[$code][(string) $pattern] = (string) $pattern['type'];
896
                if (PHP_CodeSniffer_VERBOSITY > 1) {
897
                    echo str_repeat("\t", $depth);
898
                    echo "\t\t=> added rule-specific ".(string) $pattern['type'].' ignore pattern';
899
                    if ($code !== $ref) {
900
                        echo " for $code";
901
                    }
902
903
                    echo ': '.(string) $pattern.PHP_EOL;
904
                }
905
            }//end foreach
906
907
            // Include patterns.
908 View Code Duplication
            foreach ($rule->{'include-pattern'} as $pattern) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
909
                if ($this->shouldProcessElement($pattern) === false) {
910
                    continue;
911
                }
912
913
                if (isset($this->includePatterns[$code]) === false) {
914
                    $this->includePatterns[$code] = array();
915
                }
916
917
                if (isset($pattern['type']) === false) {
918
                    $pattern['type'] = 'absolute';
919
                }
920
921
                $this->includePatterns[$code][(string) $pattern] = (string) $pattern['type'];
922
                if (PHP_CodeSniffer_VERBOSITY > 1) {
923
                    echo str_repeat("\t", $depth);
924
                    echo "\t\t=> added rule-specific ".(string) $pattern['type'].' include pattern';
925
                    if ($code !== $ref) {
926
                        echo " for $code";
927
                    }
928
929
                    echo ': '.(string) $pattern.PHP_EOL;
930
                }
931
            }//end foreach
932
        }//end foreach
933
934
    }//end processRule()
935
936
937
    /**
938
     * Determine if an element should be processed or ignored.
939
     *
940
     * @param SimpleXMLElement $element An object from a ruleset XML file.
941
     *
942
     * @return bool
943
     */
944
    private function shouldProcessElement($element)
945
    {
946
        if (isset($element['phpcbf-only']) === false
947
            && isset($element['phpcs-only']) === false
948
        ) {
949
            // No exceptions are being made.
950
            return true;
951
        }
952
953 View Code Duplication
        if (PHP_CodeSniffer_CBF === true
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
954
            && isset($element['phpcbf-only']) === true
955
            && (string) $element['phpcbf-only'] === 'true'
956
        ) {
957
            return true;
958
        }
959
960 View Code Duplication
        if (PHP_CodeSniffer_CBF === false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
961
            && isset($element['phpcs-only']) === true
962
            && (string) $element['phpcs-only'] === 'true'
963
        ) {
964
            return true;
965
        }
966
967
        return false;
968
969
    }//end shouldProcessElement()
970
971
972
    /**
973
     * Loads and stores sniffs objects used for sniffing files.
974
     *
975
     * @param array $files        Paths to the sniff files to register.
976
     * @param array $restrictions The sniff class names to restrict the allowed
977
     *                            listeners to.
978
     *
979
     * @return void
980
     */
981
    public function registerSniffs($files, $restrictions)
982
    {
983
        $listeners = array();
984
985
        foreach ($files as $file) {
986
            // Work out where the position of /StandardName/Sniffs/... is
987
            // so we can determine what the class will be called.
988
            $sniffPos = strrpos($file, DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR);
989
            if ($sniffPos === false) {
990
                continue;
991
            }
992
993
            $slashPos = strrpos(substr($file, 0, $sniffPos), DIRECTORY_SEPARATOR);
994
            if ($slashPos === false) {
995
                continue;
996
            }
997
998
            $className = Autoload::loadFile($file);
999
1000
            // If they have specified a list of sniffs to restrict to, check
1001
            // to see if this sniff is allowed.
1002
            if (empty($restrictions) === false
1003
                && isset($restrictions[strtolower($className)]) === false
1004
            ) {
1005
                continue;
1006
            }
1007
1008
            // Skip abstract classes.
1009
            $reflection = new \ReflectionClass($className);
1010
            if ($reflection->isAbstract() === true) {
1011
                continue;
1012
            }
1013
1014
            $listeners[$className] = $className;
1015
1016
            if (PHP_CodeSniffer_VERBOSITY > 2) {
1017
                echo "Registered $className".PHP_EOL;
1018
            }
1019
        }//end foreach
1020
1021
        $this->sniffs = $listeners;
1022
1023
    }//end registerSniffs()
1024
1025
1026
    /**
1027
     * Populates the array of Symplify\PHP7_CodeSniffer_Sniff's for this file.
1028
     *
1029
     * @return void
1030
     * @throws RuntimeException If sniff registration fails.
1031
     */
1032
    public function populateTokenListeners()
1033
    {
1034
        // Construct a list of listeners indexed by token being listened for.
1035
        $this->tokenListeners = array();
1036
1037
        foreach ($this->sniffs as $sniffClass => $sniffObject) {
1038
            $this->sniffs[$sniffClass] = null;
1039
            $this->sniffs[$sniffClass] = new $sniffClass();
1040
1041
            $sniffCode = Util\Common::getSniffCode($sniffClass);
1042
            $this->sniffCodes[$sniffCode] = $sniffClass;
1043
1044
            // Set custom properties.
1045
            if (isset($this->ruleset[$sniffCode]['properties']) === true) {
1046
                foreach ($this->ruleset[$sniffCode]['properties'] as $name => $value) {
1047
                    $this->setSniffProperty($sniffClass, $name, $value);
1048
                }
1049
            }
1050
1051
            $tokenizers = array();
1052
            $vars       = get_class_vars($sniffClass);
1053
            if (isset($vars['supportedTokenizers']) === true) {
1054
                foreach ($vars['supportedTokenizers'] as $tokenizer) {
1055
                    $tokenizers[$tokenizer] = $tokenizer;
1056
                }
1057
            } else {
1058
                $tokenizers = array('PHP' => 'PHP');
1059
            }
1060
1061
            $tokens = $this->sniffs[$sniffClass]->register();
1062
            if (is_array($tokens) === false) {
1063
                $msg = "Sniff $sniffClass register() method must return an array";
1064
                throw new RuntimeException($msg);
1065
            }
1066
1067
            $ignorePatterns = array();
1068
            $patterns       = $this->getIgnorePatterns($sniffCode);
1069 View Code Duplication
            foreach ($patterns as $pattern => $type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1070
                $replacements = array(
1071
                                 '\\,' => ',',
1072
                                 '*'   => '.*',
1073
                                );
1074
1075
                $ignorePatterns[] = strtr($pattern, $replacements);
1076
            }
1077
1078
            $includePatterns = array();
1079
            $patterns        = $this->getIncludePatterns($sniffCode);
1080 View Code Duplication
            foreach ($patterns as $pattern => $type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1081
                $replacements = array(
1082
                                 '\\,' => ',',
1083
                                 '*'   => '.*',
1084
                                );
1085
1086
                $includePatterns[] = strtr($pattern, $replacements);
1087
            }
1088
1089
            foreach ($tokens as $token) {
1090
                if (isset($this->tokenListeners[$token]) === false) {
1091
                    $this->tokenListeners[$token] = array();
1092
                }
1093
1094
                if (isset($this->tokenListeners[$token][$sniffClass]) === false) {
1095
                    $this->tokenListeners[$token][$sniffClass] = array(
1096
                                                                  'class'      => $sniffClass,
1097
                                                                  'source'     => $sniffCode,
1098
                                                                  'tokenizers' => $tokenizers,
1099
                                                                  'ignore'     => $ignorePatterns,
1100
                                                                  'include'    => $includePatterns,
1101
                                                                 );
1102
                }
1103
            }
1104
        }//end foreach
1105
1106
    }//end populateTokenListeners()
1107
1108
1109
    /**
1110
     * Set a single property for a sniff.
1111
     *
1112
     * @param string $sniffClass The class name of the sniff.
1113
     * @param string $name       The name of the property to change.
1114
     * @param string $value      The new value of the property.
1115
     *
1116
     * @return void
1117
     */
1118
    public function setSniffProperty($sniffClass, $name, $value)
1119
    {
1120
        // Setting a property for a sniff we are not using.
1121
        if (isset($this->sniffs[$sniffClass]) === false) {
1122
            return;
1123
        }
1124
1125
        $name = trim($name);
1126
        if (is_string($value) === true) {
1127
            $value = trim($value);
1128
        }
1129
1130
        // Special case for booleans.
1131
        if ($value === 'true') {
1132
            $value = true;
1133
        } else if ($value === 'false') {
1134
            $value = false;
1135
        }
1136
1137
        $this->sniffs[$sniffClass]->$name = $value;
1138
1139
    }//end setSniffProperty()
1140
1141
1142
    /**
1143
     * Gets the array of ignore patterns.
1144
     *
1145
     * Optionally takes a listener to get ignore patterns specified
1146
     * for that sniff only.
1147
     *
1148
     * @param string $listener The listener to get patterns for. If NULL, all
1149
     *                         patterns are returned.
1150
     *
1151
     * @return array
1152
     */
1153 View Code Duplication
    public function getIgnorePatterns($listener=null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1154
    {
1155
        if ($listener === null) {
1156
            return $this->ignorePatterns;
1157
        }
1158
1159
        if (isset($this->ignorePatterns[$listener]) === true) {
1160
            return $this->ignorePatterns[$listener];
1161
        }
1162
1163
        return array();
1164
1165
    }//end getIgnorePatterns()
1166
1167
1168
    /**
1169
     * Gets the array of include patterns.
1170
     *
1171
     * Optionally takes a listener to get include patterns specified
1172
     * for that sniff only.
1173
     *
1174
     * @param string $listener The listener to get patterns for. If NULL, all
1175
     *                         patterns are returned.
1176
     *
1177
     * @return array
1178
     */
1179 View Code Duplication
    public function getIncludePatterns($listener=null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1180
    {
1181
        if ($listener === null) {
1182
            return $this->includePatterns;
1183
        }
1184
1185
        if (isset($this->includePatterns[$listener]) === true) {
1186
            return $this->includePatterns[$listener];
1187
        }
1188
1189
        return array();
1190
1191
    }//end getIncludePatterns()
1192
1193
1194
}//end class
1195