1
|
|
|
<?php |
|
|
|
|
2
|
|
|
/** |
3
|
|
|
* PHP_CodeSniffer tokenizes PHP code and detects violations of a |
4
|
|
|
* defined set of coding standards. |
5
|
|
|
* |
6
|
|
|
* PHP version 5 |
7
|
|
|
* |
8
|
|
|
* @category PHP |
9
|
|
|
* @package PHP_CodeSniffer |
10
|
|
|
* @author Greg Sherwood <[email protected]> |
11
|
|
|
* @author Marc McIntyre <[email protected]> |
12
|
|
|
* @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) |
13
|
|
|
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence |
14
|
|
|
* @link http://pear.php.net/package/PHP_CodeSniffer |
15
|
|
|
*/ |
16
|
|
|
|
17
|
|
|
spl_autoload_register(array('PHP_CodeSniffer', 'autoload')); |
18
|
|
|
|
19
|
|
|
if (class_exists('PHP_CodeSniffer_Exception', true) === false) { |
20
|
|
|
throw new Exception('Class PHP_CodeSniffer_Exception not found'); |
21
|
|
|
} |
22
|
|
|
|
23
|
|
|
if (class_exists('PHP_CodeSniffer_File', true) === false) { |
24
|
|
|
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_File not found'); |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
if (class_exists('PHP_CodeSniffer_Fixer', true) === false) { |
28
|
|
|
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Fixer not found'); |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
if (class_exists('PHP_CodeSniffer_Tokens', true) === false) { |
32
|
|
|
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Tokens not found'); |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
if (class_exists('PHP_CodeSniffer_CLI', true) === false) { |
36
|
|
|
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_CLI not found'); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
if (interface_exists('PHP_CodeSniffer_Sniff', true) === false) { |
40
|
|
|
throw new PHP_CodeSniffer_Exception('Interface PHP_CodeSniffer_Sniff not found'); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* PHP_CodeSniffer tokenizes PHP code and detects violations of a |
45
|
|
|
* defined set of coding standards. |
46
|
|
|
* |
47
|
|
|
* Standards are specified by classes that implement the PHP_CodeSniffer_Sniff |
48
|
|
|
* interface. A sniff registers what token types it wishes to listen for, then |
49
|
|
|
* PHP_CodeSniffer encounters that token, the sniff is invoked and passed |
50
|
|
|
* information about where the token was found in the stack, and the token stack |
51
|
|
|
* itself. |
52
|
|
|
* |
53
|
|
|
* Sniff files and their containing class must be prefixed with Sniff, and |
54
|
|
|
* have an extension of .php. |
55
|
|
|
* |
56
|
|
|
* Multiple PHP_CodeSniffer operations can be performed by re-calling the |
57
|
|
|
* process function with different parameters. |
58
|
|
|
* |
59
|
|
|
* @category PHP |
60
|
|
|
* @package PHP_CodeSniffer |
61
|
|
|
* @author Greg Sherwood <[email protected]> |
62
|
|
|
* @author Marc McIntyre <[email protected]> |
63
|
|
|
* @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) |
64
|
|
|
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence |
65
|
|
|
* @version Release: @package_version@ |
66
|
|
|
* @link http://pear.php.net/package/PHP_CodeSniffer |
67
|
|
|
*/ |
68
|
|
|
class PHP_CodeSniffer |
|
|
|
|
69
|
|
|
{ |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* The current version. |
73
|
|
|
* |
74
|
|
|
* @var string |
75
|
|
|
*/ |
76
|
|
|
const VERSION = '2.7.1'; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Package stability; either stable, beta or alpha. |
80
|
|
|
* |
81
|
|
|
* @var string |
82
|
|
|
*/ |
83
|
|
|
const STABILITY = 'stable'; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* The file or directory that is currently being processed. |
87
|
|
|
* |
88
|
|
|
* @var string |
89
|
|
|
*/ |
90
|
|
|
protected $file = ''; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* The directories that the processed rulesets are in. |
94
|
|
|
* |
95
|
|
|
* This is declared static because it is also used in the |
96
|
|
|
* autoloader to look for sniffs outside the PHPCS install. |
97
|
|
|
* This way, standards designed to be installed inside PHPCS can |
98
|
|
|
* also be used from outside the PHPCS Standards directory. |
99
|
|
|
* |
100
|
|
|
* @var string |
101
|
|
|
*/ |
102
|
|
|
protected static $rulesetDirs = array(); |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* The CLI object controlling the run. |
106
|
|
|
* |
107
|
|
|
* @var PHP_CodeSniffer_CLI |
108
|
|
|
*/ |
109
|
|
|
public $cli = null; |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* The Reporting object controlling report generation. |
113
|
|
|
* |
114
|
|
|
* @var PHP_CodeSniffer_Reporting |
115
|
|
|
*/ |
116
|
|
|
public $reporting = null; |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* An array of sniff objects that are being used to check files. |
120
|
|
|
* |
121
|
|
|
* @var array(PHP_CodeSniffer_Sniff) |
122
|
|
|
*/ |
123
|
|
|
protected $listeners = array(); |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* An array of sniffs that are being used to check files. |
127
|
|
|
* |
128
|
|
|
* @var array(string) |
129
|
|
|
*/ |
130
|
|
|
protected $sniffs = array(); |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* A mapping of sniff codes to fully qualified class names. |
134
|
|
|
* |
135
|
|
|
* The key is the sniff code and the value |
136
|
|
|
* is the fully qualified name of the sniff class. |
137
|
|
|
* |
138
|
|
|
* @var array<string, string> |
139
|
|
|
*/ |
140
|
|
|
public $sniffCodes = array(); |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* The listeners array, indexed by token type. |
144
|
|
|
* |
145
|
|
|
* @var array |
146
|
|
|
*/ |
147
|
|
|
private $_tokenListeners = array(); |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* An array of rules from the ruleset.xml file. |
151
|
|
|
* |
152
|
|
|
* It may be empty, indicating that the ruleset does not override |
153
|
|
|
* any of the default sniff settings. |
154
|
|
|
* |
155
|
|
|
* @var array |
156
|
|
|
*/ |
157
|
|
|
protected $ruleset = array(); |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* An array of patterns to use for skipping files. |
161
|
|
|
* |
162
|
|
|
* @var array |
163
|
|
|
*/ |
164
|
|
|
protected $ignorePatterns = array(); |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* An array of extensions for files we will check. |
168
|
|
|
* |
169
|
|
|
* @var array |
170
|
|
|
*/ |
171
|
|
|
public $allowedFileExtensions = array(); |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* An array of default extensions and associated tokenizers. |
175
|
|
|
* |
176
|
|
|
* If no extensions are set, these will be used as the defaults. |
177
|
|
|
* If extensions are set, these will be used when the correct tokenizer |
178
|
|
|
* can not be determined, such as when checking a passed filename instead |
179
|
|
|
* of files in a directory. |
180
|
|
|
* |
181
|
|
|
* @var array |
182
|
|
|
*/ |
183
|
|
|
public $defaultFileExtensions = array( |
184
|
|
|
'php' => 'PHP', |
185
|
|
|
'inc' => 'PHP', |
186
|
|
|
'js' => 'JS', |
187
|
|
|
'css' => 'CSS', |
188
|
|
|
); |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* An array of variable types for param/var we will check. |
192
|
|
|
* |
193
|
|
|
* @var array(string) |
194
|
|
|
*/ |
195
|
|
|
public static $allowedTypes = array( |
196
|
|
|
'array', |
197
|
|
|
'boolean', |
198
|
|
|
'float', |
199
|
|
|
'integer', |
200
|
|
|
'mixed', |
201
|
|
|
'object', |
202
|
|
|
'string', |
203
|
|
|
'resource', |
204
|
|
|
'callable', |
205
|
|
|
); |
206
|
|
|
|
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Constructs a PHP_CodeSniffer object. |
210
|
|
|
* |
211
|
|
|
* @param int $verbosity The verbosity level. |
212
|
|
|
* 1: Print progress information. |
213
|
|
|
* 2: Print tokenizer debug information. |
214
|
|
|
* 3: Print sniff debug information. |
215
|
|
|
* @param int $tabWidth The number of spaces each tab represents. |
216
|
|
|
* If greater than zero, tabs will be replaced |
217
|
|
|
* by spaces before testing each file. |
218
|
|
|
* @param string $encoding The charset of the sniffed files. |
219
|
|
|
* This is important for some reports that output |
220
|
|
|
* with utf-8 encoding as you don't want it double |
221
|
|
|
* encoding messages. |
222
|
|
|
* @param bool $interactive If TRUE, will stop after each file with errors |
223
|
|
|
* and wait for user input. |
224
|
|
|
* |
225
|
|
|
* @see process() |
226
|
|
|
*/ |
227
|
|
|
public function __construct( |
228
|
|
|
$verbosity=0, |
229
|
|
|
$tabWidth=0, |
230
|
|
|
$encoding='iso-8859-1', |
231
|
|
|
$interactive=false |
232
|
|
|
) { |
233
|
|
|
if ($verbosity !== null) { |
234
|
|
|
$this->setVerbosity($verbosity); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
if ($tabWidth !== null) { |
238
|
|
|
$this->setTabWidth($tabWidth); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
if ($encoding !== null) { |
242
|
|
|
$this->setEncoding($encoding); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
if ($interactive !== null) { |
246
|
|
|
$this->setInteractive($interactive); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
if (defined('PHPCS_DEFAULT_ERROR_SEV') === false) { |
250
|
|
|
define('PHPCS_DEFAULT_ERROR_SEV', 5); |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
if (defined('PHPCS_DEFAULT_WARN_SEV') === false) { |
254
|
|
|
define('PHPCS_DEFAULT_WARN_SEV', 5); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
if (defined('PHP_CODESNIFFER_CBF') === false) { |
258
|
|
|
define('PHP_CODESNIFFER_CBF', false); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
// Set default CLI object in case someone is running us |
262
|
|
|
// without using the command line script. |
263
|
|
|
$this->cli = new PHP_CodeSniffer_CLI(); |
264
|
|
|
$this->cli->errorSeverity = PHPCS_DEFAULT_ERROR_SEV; |
|
|
|
|
265
|
|
|
$this->cli->warningSeverity = PHPCS_DEFAULT_WARN_SEV; |
|
|
|
|
266
|
|
|
$this->cli->dieOnUnknownArg = false; |
267
|
|
|
|
268
|
|
|
$this->reporting = new PHP_CodeSniffer_Reporting(); |
269
|
|
|
|
270
|
|
|
}//end __construct() |
271
|
|
|
|
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* Autoload static method for loading classes and interfaces. |
275
|
|
|
* |
276
|
|
|
* @param string $className The name of the class or interface. |
277
|
|
|
* |
278
|
|
|
* @return void |
279
|
|
|
*/ |
280
|
|
|
public static function autoload($className) |
281
|
|
|
{ |
282
|
|
|
if (substr($className, 0, 4) === 'PHP_') { |
283
|
|
|
$newClassName = substr($className, 4); |
284
|
|
|
} else { |
285
|
|
|
$newClassName = $className; |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
$path = str_replace(array('_', '\\'), DIRECTORY_SEPARATOR, $newClassName).'.php'; |
289
|
|
|
|
290
|
|
|
if (is_file(dirname(__FILE__).DIRECTORY_SEPARATOR.$path) === true) { |
291
|
|
|
// Check standard file locations based on class name. |
292
|
|
|
include dirname(__FILE__).DIRECTORY_SEPARATOR.$path; |
293
|
|
|
return; |
294
|
|
|
} else { |
295
|
|
|
// Check for included sniffs. |
296
|
|
|
$installedPaths = PHP_CodeSniffer::getInstalledStandardPaths(); |
297
|
|
|
foreach ($installedPaths as $installedPath) { |
298
|
|
|
if (is_file($installedPath.DIRECTORY_SEPARATOR.$path) === true) { |
299
|
|
|
include $installedPath.DIRECTORY_SEPARATOR.$path; |
300
|
|
|
return; |
301
|
|
|
} |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
// Check standard file locations based on the loaded rulesets. |
305
|
|
|
foreach (self::$rulesetDirs as $rulesetDir) { |
|
|
|
|
306
|
|
|
if (is_file(dirname($rulesetDir).DIRECTORY_SEPARATOR.$path) === true) { |
307
|
|
|
include_once dirname($rulesetDir).DIRECTORY_SEPARATOR.$path; |
308
|
|
|
return; |
309
|
|
|
} |
310
|
|
|
} |
311
|
|
|
}//end if |
312
|
|
|
|
313
|
|
|
// Everything else. |
314
|
|
|
@include $path; |
|
|
|
|
315
|
|
|
|
316
|
|
|
}//end autoload() |
317
|
|
|
|
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Sets the verbosity level. |
321
|
|
|
* |
322
|
|
|
* @param int $verbosity The verbosity level. |
323
|
|
|
* 1: Print progress information. |
324
|
|
|
* 2: Print tokenizer debug information. |
325
|
|
|
* 3: Print sniff debug information. |
326
|
|
|
* |
327
|
|
|
* @return void |
328
|
|
|
*/ |
329
|
|
|
public function setVerbosity($verbosity) |
330
|
|
|
{ |
331
|
|
|
if (defined('PHP_CODESNIFFER_VERBOSITY') === false) { |
332
|
|
|
define('PHP_CODESNIFFER_VERBOSITY', $verbosity); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
}//end setVerbosity() |
336
|
|
|
|
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* Sets the tab width. |
340
|
|
|
* |
341
|
|
|
* @param int $tabWidth The number of spaces each tab represents. |
342
|
|
|
* If greater than zero, tabs will be replaced |
343
|
|
|
* by spaces before testing each file. |
344
|
|
|
* |
345
|
|
|
* @return void |
346
|
|
|
*/ |
347
|
|
|
public function setTabWidth($tabWidth) |
348
|
|
|
{ |
349
|
|
|
if (defined('PHP_CODESNIFFER_TAB_WIDTH') === false) { |
350
|
|
|
define('PHP_CODESNIFFER_TAB_WIDTH', $tabWidth); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
}//end setTabWidth() |
354
|
|
|
|
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* Sets the encoding. |
358
|
|
|
* |
359
|
|
|
* @param string $encoding The charset of the sniffed files. |
360
|
|
|
* This is important for some reports that output |
361
|
|
|
* with utf-8 encoding as you don't want it double |
362
|
|
|
* encoding messages. |
363
|
|
|
* |
364
|
|
|
* @return void |
365
|
|
|
*/ |
366
|
|
|
public function setEncoding($encoding) |
367
|
|
|
{ |
368
|
|
|
if (defined('PHP_CODESNIFFER_ENCODING') === false) { |
369
|
|
|
define('PHP_CODESNIFFER_ENCODING', $encoding); |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
}//end setEncoding() |
373
|
|
|
|
374
|
|
|
|
375
|
|
|
/** |
376
|
|
|
* Sets the interactive flag. |
377
|
|
|
* |
378
|
|
|
* @param bool $interactive If TRUE, will stop after each file with errors |
379
|
|
|
* and wait for user input. |
380
|
|
|
* |
381
|
|
|
* @return void |
382
|
|
|
*/ |
383
|
|
|
public function setInteractive($interactive) |
384
|
|
|
{ |
385
|
|
|
if (defined('PHP_CODESNIFFER_INTERACTIVE') === false) { |
386
|
|
|
define('PHP_CODESNIFFER_INTERACTIVE', $interactive); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
}//end setInteractive() |
390
|
|
|
|
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* Sets an array of file extensions that we will allow checking of. |
394
|
|
|
* |
395
|
|
|
* If the extension is one of the defaults, a specific tokenizer |
396
|
|
|
* will be used. Otherwise, the PHP tokenizer will be used for |
397
|
|
|
* all extensions passed. |
398
|
|
|
* |
399
|
|
|
* @param array $extensions An array of file extensions. |
400
|
|
|
* |
401
|
|
|
* @return void |
402
|
|
|
*/ |
403
|
|
|
public function setAllowedFileExtensions(array $extensions) |
404
|
|
|
{ |
405
|
|
|
$newExtensions = array(); |
406
|
|
|
foreach ($extensions as $ext) { |
407
|
|
|
$slash = strpos($ext, '/'); |
408
|
|
|
if ($slash !== false) { |
409
|
|
|
// They specified the tokenizer too. |
410
|
|
|
list($ext, $tokenizer) = explode('/', $ext); |
411
|
|
|
$newExtensions[$ext] = strtoupper($tokenizer); |
412
|
|
|
continue; |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
if (isset($this->allowedFileExtensions[$ext]) === true) { |
416
|
|
|
$newExtensions[$ext] = $this->allowedFileExtensions[$ext]; |
417
|
|
|
} else if (isset($this->defaultFileExtensions[$ext]) === true) { |
418
|
|
|
$newExtensions[$ext] = $this->defaultFileExtensions[$ext]; |
419
|
|
|
} else { |
420
|
|
|
$newExtensions[$ext] = 'PHP'; |
421
|
|
|
} |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
$this->allowedFileExtensions = $newExtensions; |
425
|
|
|
|
426
|
|
|
}//end setAllowedFileExtensions() |
427
|
|
|
|
428
|
|
|
|
429
|
|
|
/** |
430
|
|
|
* Sets an array of ignore patterns that we use to skip files and folders. |
431
|
|
|
* |
432
|
|
|
* Patterns are not case sensitive. |
433
|
|
|
* |
434
|
|
|
* @param array $patterns An array of ignore patterns. The pattern is the key |
435
|
|
|
* and the value is either "absolute" or "relative", |
436
|
|
|
* depending on how the pattern should be applied to a |
437
|
|
|
* file path. |
438
|
|
|
* |
439
|
|
|
* @return void |
440
|
|
|
*/ |
441
|
|
|
public function setIgnorePatterns(array $patterns) |
442
|
|
|
{ |
443
|
|
|
$this->ignorePatterns = $patterns; |
444
|
|
|
|
445
|
|
|
}//end setIgnorePatterns() |
446
|
|
|
|
447
|
|
|
|
448
|
|
|
/** |
449
|
|
|
* Gets the array of ignore patterns. |
450
|
|
|
* |
451
|
|
|
* Optionally takes a listener to get ignore patterns specified |
452
|
|
|
* for that sniff only. |
453
|
|
|
* |
454
|
|
|
* @param string $listener The listener to get patterns for. If NULL, all |
455
|
|
|
* patterns are returned. |
456
|
|
|
* |
457
|
|
|
* @return array |
458
|
|
|
*/ |
459
|
|
|
public function getIgnorePatterns($listener=null) |
460
|
|
|
{ |
461
|
|
|
if ($listener === null) { |
462
|
|
|
return $this->ignorePatterns; |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
if (isset($this->ignorePatterns[$listener]) === true) { |
466
|
|
|
return $this->ignorePatterns[$listener]; |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
return array(); |
470
|
|
|
|
471
|
|
|
}//end getIgnorePatterns() |
472
|
|
|
|
473
|
|
|
|
474
|
|
|
/** |
475
|
|
|
* Sets the internal CLI object. |
476
|
|
|
* |
477
|
|
|
* @param object $cli The CLI object controlling the run. |
478
|
|
|
* |
479
|
|
|
* @return void |
480
|
|
|
*/ |
481
|
|
|
public function setCli($cli) |
482
|
|
|
{ |
483
|
|
|
$this->cli = $cli; |
484
|
|
|
|
485
|
|
|
}//end setCli() |
486
|
|
|
|
487
|
|
|
|
488
|
|
|
/** |
489
|
|
|
* Start a PHP_CodeSniffer run. |
490
|
|
|
* |
491
|
|
|
* @param string|array $files The files and directories to process. For |
492
|
|
|
* directories, each sub directory will also |
493
|
|
|
* be traversed for source files. |
494
|
|
|
* @param string|array $standards The set of code sniffs we are testing |
495
|
|
|
* against. |
496
|
|
|
* @param array $restrictions The sniff codes to restrict the |
497
|
|
|
* violations to. |
498
|
|
|
* @param boolean $local If true, don't recurse into directories. |
499
|
|
|
* |
500
|
|
|
* @return void |
501
|
|
|
*/ |
502
|
|
|
public function process($files, $standards, array $restrictions=array(), $local=false) |
503
|
|
|
{ |
504
|
|
|
$files = (array) $files; |
505
|
|
|
$this->initStandard($standards, $restrictions); |
506
|
|
|
$this->processFiles($files, $local); |
507
|
|
|
|
508
|
|
|
}//end process() |
509
|
|
|
|
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* Initialise the standard that the run will use. |
513
|
|
|
* |
514
|
|
|
* @param string|array $standards The set of code sniffs we are testing |
515
|
|
|
* against. |
516
|
|
|
* @param array $restrictions The sniff codes to restrict the testing to. |
517
|
|
|
* @param array $exclusions The sniff codes to exclude from testing. |
518
|
|
|
* |
519
|
|
|
* @return void |
520
|
|
|
*/ |
521
|
|
|
public function initStandard($standards, array $restrictions=array(), array $exclusions=array()) |
522
|
|
|
{ |
523
|
|
|
$standards = (array) $standards; |
524
|
|
|
|
525
|
|
|
// Reset the members. |
526
|
|
|
$this->listeners = array(); |
527
|
|
|
$this->sniffs = array(); |
528
|
|
|
$this->ruleset = array(); |
529
|
|
|
$this->_tokenListeners = array(); |
530
|
|
|
self::$rulesetDirs = array(); |
|
|
|
|
531
|
|
|
|
532
|
|
|
// Ensure this option is enabled or else line endings will not always |
533
|
|
|
// be detected properly for files created on a Mac with the /r line ending. |
534
|
|
|
ini_set('auto_detect_line_endings', true); |
535
|
|
|
|
536
|
|
|
if (defined('PHP_CODESNIFFER_IN_TESTS') === true && empty($restrictions) === false) { |
537
|
|
|
// Should be one standard and one sniff being tested at a time. |
538
|
|
|
$installed = $this->getInstalledStandardPath($standards[0]); |
539
|
|
View Code Duplication |
if ($installed !== null) { |
|
|
|
|
540
|
|
|
$standard = $installed; |
541
|
|
|
} else { |
542
|
|
|
$standard = self::realpath($standards[0]); |
543
|
|
|
if (is_dir($standard) === true |
544
|
|
|
&& is_file(self::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml')) === true |
545
|
|
|
) { |
546
|
|
|
$standard = self::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml'); |
547
|
|
|
} |
548
|
|
|
} |
549
|
|
|
|
550
|
|
|
$sniffs = $this->_expandRulesetReference($restrictions[0], dirname($standard)); |
551
|
|
|
} else { |
552
|
|
|
$sniffs = array(); |
553
|
|
|
foreach ($standards as $standard) { |
554
|
|
|
$installed = $this->getInstalledStandardPath($standard); |
555
|
|
View Code Duplication |
if ($installed !== null) { |
|
|
|
|
556
|
|
|
$standard = $installed; |
557
|
|
|
} else { |
558
|
|
|
$standard = self::realpath($standard); |
559
|
|
|
if (is_dir($standard) === true |
560
|
|
|
&& is_file(self::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml')) === true |
561
|
|
|
) { |
562
|
|
|
$standard = self::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml'); |
563
|
|
|
} |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
if (PHP_CODESNIFFER_VERBOSITY === 1) { |
567
|
|
|
$ruleset = simplexml_load_string(file_get_contents($standard)); |
568
|
|
|
if ($ruleset !== false) { |
569
|
|
|
$standardName = (string) $ruleset['name']; |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
echo "Registering sniffs in the $standardName standard... "; |
|
|
|
|
573
|
|
|
if (count($standards) > 1 || PHP_CODESNIFFER_VERBOSITY > 2) { |
574
|
|
|
echo PHP_EOL; |
575
|
|
|
} |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
$sniffs = array_merge($sniffs, $this->processRuleset($standard)); |
|
|
|
|
579
|
|
|
}//end foreach |
580
|
|
|
}//end if |
581
|
|
|
|
582
|
|
|
$sniffRestrictions = array(); |
583
|
|
View Code Duplication |
foreach ($restrictions as $sniffCode) { |
|
|
|
|
584
|
|
|
$parts = explode('.', strtolower($sniffCode)); |
585
|
|
|
$sniffRestrictions[] = $parts[0].'_sniffs_'.$parts[1].'_'.$parts[2].'sniff'; |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
$sniffExclusions = array(); |
589
|
|
View Code Duplication |
foreach ($exclusions as $sniffCode) { |
|
|
|
|
590
|
|
|
$parts = explode('.', strtolower($sniffCode)); |
591
|
|
|
$sniffExclusions[] = $parts[0].'_sniffs_'.$parts[1].'_'.$parts[2].'sniff'; |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
$this->registerSniffs($sniffs, $sniffRestrictions, $sniffExclusions); |
595
|
|
|
$this->populateTokenListeners(); |
596
|
|
|
|
597
|
|
|
if (PHP_CODESNIFFER_VERBOSITY === 1) { |
598
|
|
|
$numSniffs = count($this->sniffs); |
599
|
|
|
echo "DONE ($numSniffs sniffs registered)".PHP_EOL; |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
}//end initStandard() |
603
|
|
|
|
604
|
|
|
|
605
|
|
|
/** |
606
|
|
|
* Processes the files/directories that PHP_CodeSniffer was constructed with. |
607
|
|
|
* |
608
|
|
|
* @param string|array $files The files and directories to process. For |
609
|
|
|
* directories, each sub directory will also |
610
|
|
|
* be traversed for source files. |
611
|
|
|
* @param boolean $local If true, don't recurse into directories. |
612
|
|
|
* |
613
|
|
|
* @return void |
614
|
|
|
* @throws PHP_CodeSniffer_Exception If files are invalid. |
615
|
|
|
*/ |
616
|
|
|
public function processFiles($files, $local=false) |
617
|
|
|
{ |
618
|
|
|
$files = (array) $files; |
619
|
|
|
$cliValues = $this->cli->getCommandLineValues(); |
620
|
|
|
$showProgress = $cliValues['showProgress']; |
621
|
|
|
$useColors = $cliValues['colors']; |
622
|
|
|
|
623
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 0) { |
624
|
|
|
echo 'Creating file list... '; |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
if (empty($this->allowedFileExtensions) === true) { |
628
|
|
|
$this->allowedFileExtensions = $this->defaultFileExtensions; |
629
|
|
|
} |
630
|
|
|
|
631
|
|
|
$todo = $this->getFilesToProcess($files, $local); |
|
|
|
|
632
|
|
|
$numFiles = count($todo); |
633
|
|
|
|
634
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 0) { |
635
|
|
|
echo "DONE ($numFiles files in queue)".PHP_EOL; |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
$numProcessed = 0; |
639
|
|
|
$dots = 0; |
640
|
|
|
$maxLength = strlen($numFiles); |
641
|
|
|
$lastDir = ''; |
642
|
|
|
foreach ($todo as $file) { |
643
|
|
|
$this->file = $file; |
644
|
|
|
$currDir = dirname($file); |
645
|
|
|
if ($lastDir !== $currDir) { |
646
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 0 || PHP_CODESNIFFER_CBF === true) { |
647
|
|
|
echo 'Changing into directory '.$currDir.PHP_EOL; |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
$lastDir = $currDir; |
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
$phpcsFile = $this->processFile($file, null); |
654
|
|
|
$numProcessed++; |
655
|
|
|
|
656
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 0 |
657
|
|
|
|| PHP_CODESNIFFER_INTERACTIVE === true |
658
|
|
|
|| $showProgress === false |
659
|
|
|
) { |
660
|
|
|
continue; |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
// Show progress information. |
664
|
|
|
if ($phpcsFile === null) { |
665
|
|
|
echo 'S'; |
666
|
|
|
} else { |
667
|
|
|
$errors = $phpcsFile->getErrorCount(); |
668
|
|
|
$warnings = $phpcsFile->getWarningCount(); |
669
|
|
|
if ($errors > 0) { |
670
|
|
|
if ($useColors === true) { |
671
|
|
|
echo "\033[31m"; |
672
|
|
|
} |
673
|
|
|
|
674
|
|
|
echo 'E'; |
675
|
|
|
} else if ($warnings > 0) { |
676
|
|
|
if ($useColors === true) { |
677
|
|
|
echo "\033[33m"; |
678
|
|
|
} |
679
|
|
|
|
680
|
|
|
echo 'W'; |
681
|
|
|
} else { |
682
|
|
|
echo '.'; |
683
|
|
|
} |
684
|
|
|
|
685
|
|
|
if ($useColors === true) { |
686
|
|
|
echo "\033[0m"; |
687
|
|
|
} |
688
|
|
|
}//end if |
689
|
|
|
|
690
|
|
|
$dots++; |
691
|
|
|
if ($dots === 60) { |
692
|
|
|
$padding = ($maxLength - strlen($numProcessed)); |
693
|
|
|
echo str_repeat(' ', $padding); |
694
|
|
|
$percent = round(($numProcessed / $numFiles) * 100); |
695
|
|
|
echo " $numProcessed / $numFiles ($percent%)".PHP_EOL; |
696
|
|
|
$dots = 0; |
697
|
|
|
} |
698
|
|
|
}//end foreach |
699
|
|
|
|
700
|
|
|
if (PHP_CODESNIFFER_VERBOSITY === 0 |
701
|
|
|
&& PHP_CODESNIFFER_INTERACTIVE === false |
702
|
|
|
&& $showProgress === true |
703
|
|
|
) { |
704
|
|
|
echo PHP_EOL.PHP_EOL; |
705
|
|
|
} |
706
|
|
|
|
707
|
|
|
}//end processFiles() |
708
|
|
|
|
709
|
|
|
|
710
|
|
|
/** |
711
|
|
|
* Processes a single ruleset and returns a list of the sniffs it represents. |
712
|
|
|
* |
713
|
|
|
* Rules founds within the ruleset are processed immediately, but sniff classes |
714
|
|
|
* are not registered by this method. |
715
|
|
|
* |
716
|
|
|
* @param string $rulesetPath The path to a ruleset XML file. |
717
|
|
|
* @param int $depth How many nested processing steps we are in. This |
718
|
|
|
* is only used for debug output. |
719
|
|
|
* |
720
|
|
|
* @return array |
721
|
|
|
* @throws PHP_CodeSniffer_Exception If the ruleset path is invalid. |
722
|
|
|
*/ |
723
|
|
|
public function processRuleset($rulesetPath, $depth=0) |
724
|
|
|
{ |
725
|
|
|
$rulesetPath = self::realpath($rulesetPath); |
726
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
727
|
|
|
echo str_repeat("\t", $depth); |
728
|
|
|
echo "Processing ruleset $rulesetPath".PHP_EOL; |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
$ruleset = simplexml_load_string(file_get_contents($rulesetPath)); |
732
|
|
|
if ($ruleset === false) { |
733
|
|
|
throw new PHP_CodeSniffer_Exception("Ruleset $rulesetPath is not valid"); |
734
|
|
|
} |
735
|
|
|
|
736
|
|
|
$ownSniffs = array(); |
737
|
|
|
$includedSniffs = array(); |
738
|
|
|
$excludedSniffs = array(); |
739
|
|
|
$cliValues = $this->cli->getCommandLineValues(); |
740
|
|
|
|
741
|
|
|
$rulesetDir = dirname($rulesetPath); |
742
|
|
|
self::$rulesetDirs[] = $rulesetDir; |
743
|
|
|
|
744
|
|
|
if (is_dir($rulesetDir.DIRECTORY_SEPARATOR.'Sniffs') === true) { |
745
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
746
|
|
|
echo str_repeat("\t", $depth); |
747
|
|
|
echo "\tAdding sniff files from \"/.../".basename($rulesetDir)."/Sniffs/\" directory".PHP_EOL; |
748
|
|
|
} |
749
|
|
|
|
750
|
|
|
$ownSniffs = $this->_expandSniffDirectory($rulesetDir.DIRECTORY_SEPARATOR.'Sniffs', $depth); |
751
|
|
|
} |
752
|
|
|
|
753
|
|
|
// Process custom sniff config settings. |
754
|
|
|
foreach ($ruleset->{'config'} as $config) { |
755
|
|
|
if ($this->_shouldProcessElement($config) === false) { |
756
|
|
|
continue; |
757
|
|
|
} |
758
|
|
|
|
759
|
|
|
$this->setConfigData((string) $config['name'], (string) $config['value'], true); |
760
|
|
View Code Duplication |
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
|
|
|
|
761
|
|
|
echo str_repeat("\t", $depth); |
762
|
|
|
echo "\t=> set config value ".(string) $config['name'].': '.(string) $config['value'].PHP_EOL; |
763
|
|
|
} |
764
|
|
|
} |
765
|
|
|
|
766
|
|
|
foreach ($ruleset->rule as $rule) { |
767
|
|
|
if (isset($rule['ref']) === false |
768
|
|
|
|| $this->_shouldProcessElement($rule) === false |
769
|
|
|
) { |
770
|
|
|
continue; |
771
|
|
|
} |
772
|
|
|
|
773
|
|
View Code Duplication |
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
|
|
|
|
774
|
|
|
echo str_repeat("\t", $depth); |
775
|
|
|
echo "\tProcessing rule \"".$rule['ref'].'"'.PHP_EOL; |
776
|
|
|
} |
777
|
|
|
|
778
|
|
|
$includedSniffs = array_merge( |
779
|
|
|
$includedSniffs, |
780
|
|
|
$this->_expandRulesetReference($rule['ref'], $rulesetDir, $depth) |
781
|
|
|
); |
782
|
|
|
|
783
|
|
|
if (isset($rule->exclude) === true) { |
784
|
|
|
foreach ($rule->exclude as $exclude) { |
785
|
|
|
if ($this->_shouldProcessElement($exclude) === false) { |
786
|
|
|
continue; |
787
|
|
|
} |
788
|
|
|
|
789
|
|
View Code Duplication |
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
|
|
|
|
790
|
|
|
echo str_repeat("\t", $depth); |
791
|
|
|
echo "\t\tExcluding rule \"".$exclude['name'].'"'.PHP_EOL; |
792
|
|
|
} |
793
|
|
|
|
794
|
|
|
// Check if a single code is being excluded, which is a shortcut |
795
|
|
|
// for setting the severity of the message to 0. |
796
|
|
|
$parts = explode('.', $exclude['name']); |
797
|
|
|
if (count($parts) === 4) { |
798
|
|
|
$this->ruleset[(string) $exclude['name']]['severity'] = 0; |
799
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
800
|
|
|
echo str_repeat("\t", $depth); |
801
|
|
|
echo "\t\t=> severity set to 0".PHP_EOL; |
802
|
|
|
} |
803
|
|
|
} else { |
804
|
|
|
$excludedSniffs = array_merge( |
805
|
|
|
$excludedSniffs, |
806
|
|
|
$this->_expandRulesetReference($exclude['name'], $rulesetDir, ($depth + 1)) |
807
|
|
|
); |
808
|
|
|
} |
809
|
|
|
}//end foreach |
810
|
|
|
}//end if |
811
|
|
|
|
812
|
|
|
$this->_processRule($rule, $depth); |
813
|
|
|
}//end foreach |
814
|
|
|
|
815
|
|
|
// Process custom command line arguments. |
816
|
|
|
$cliArgs = array(); |
817
|
|
|
foreach ($ruleset->{'arg'} as $arg) { |
818
|
|
|
if ($this->_shouldProcessElement($arg) === false) { |
819
|
|
|
continue; |
820
|
|
|
} |
821
|
|
|
|
822
|
|
|
if (isset($arg['name']) === true) { |
823
|
|
|
$argString = '--'.(string) $arg['name']; |
824
|
|
|
if (isset($arg['value']) === true) { |
825
|
|
|
$argString .= '='.(string) $arg['value']; |
826
|
|
|
} |
827
|
|
|
} else { |
828
|
|
|
$argString = '-'.(string) $arg['value']; |
829
|
|
|
} |
830
|
|
|
|
831
|
|
|
$cliArgs[] = $argString; |
832
|
|
|
|
833
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
834
|
|
|
echo str_repeat("\t", $depth); |
835
|
|
|
echo "\t=> set command line value $argString".PHP_EOL; |
836
|
|
|
} |
837
|
|
|
}//end foreach |
838
|
|
|
|
839
|
|
|
// Set custom php ini values as CLI args. |
840
|
|
|
foreach ($ruleset->{'ini'} as $arg) { |
841
|
|
|
if ($this->_shouldProcessElement($arg) === false) { |
842
|
|
|
continue; |
843
|
|
|
} |
844
|
|
|
|
845
|
|
|
if (isset($arg['name']) === false) { |
846
|
|
|
continue; |
847
|
|
|
} |
848
|
|
|
|
849
|
|
|
$name = (string) $arg['name']; |
850
|
|
|
$argString = $name; |
851
|
|
|
if (isset($arg['value']) === true) { |
852
|
|
|
$value = (string) $arg['value']; |
853
|
|
|
$argString .= "=$value"; |
854
|
|
|
} else { |
855
|
|
|
$value = 'true'; |
856
|
|
|
} |
857
|
|
|
|
858
|
|
|
$cliArgs[] = '-d'; |
859
|
|
|
$cliArgs[] = $argString; |
860
|
|
|
|
861
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
862
|
|
|
echo str_repeat("\t", $depth); |
863
|
|
|
echo "\t=> set PHP ini value $name to $value".PHP_EOL; |
864
|
|
|
} |
865
|
|
|
}//end foreach |
866
|
|
|
|
867
|
|
|
if (empty($cliValues['files']) === true && $cliValues['stdin'] === null) { |
868
|
|
|
// Process hard-coded file paths. |
869
|
|
|
foreach ($ruleset->{'file'} as $file) { |
870
|
|
|
$file = (string) $file; |
871
|
|
|
$cliArgs[] = $file; |
872
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
873
|
|
|
echo str_repeat("\t", $depth); |
874
|
|
|
echo "\t=> added \"$file\" to the file list".PHP_EOL; |
875
|
|
|
} |
876
|
|
|
} |
877
|
|
|
} |
878
|
|
|
|
879
|
|
|
if (empty($cliArgs) === false) { |
880
|
|
|
// Change the directory so all relative paths are worked |
881
|
|
|
// out based on the location of the ruleset instead of |
882
|
|
|
// the location of the user. |
883
|
|
|
$inPhar = self::isPharFile($rulesetDir); |
884
|
|
|
if ($inPhar === false) { |
885
|
|
|
$currentDir = getcwd(); |
886
|
|
|
chdir($rulesetDir); |
887
|
|
|
} |
888
|
|
|
|
889
|
|
|
$this->cli->setCommandLineValues($cliArgs); |
890
|
|
|
|
891
|
|
|
if ($inPhar === false) { |
892
|
|
|
chdir($currentDir); |
|
|
|
|
893
|
|
|
} |
894
|
|
|
} |
895
|
|
|
|
896
|
|
|
// Process custom ignore pattern rules. |
897
|
|
|
foreach ($ruleset->{'exclude-pattern'} as $pattern) { |
898
|
|
|
if ($this->_shouldProcessElement($pattern) === false) { |
899
|
|
|
continue; |
900
|
|
|
} |
901
|
|
|
|
902
|
|
|
if (isset($pattern['type']) === false) { |
903
|
|
|
$pattern['type'] = 'absolute'; |
904
|
|
|
} |
905
|
|
|
|
906
|
|
|
$this->ignorePatterns[(string) $pattern] = (string) $pattern['type']; |
907
|
|
View Code Duplication |
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
|
|
|
|
908
|
|
|
echo str_repeat("\t", $depth); |
909
|
|
|
echo "\t=> added global ".(string) $pattern['type'].' ignore pattern: '.(string) $pattern.PHP_EOL; |
910
|
|
|
} |
911
|
|
|
} |
912
|
|
|
|
913
|
|
|
$includedSniffs = array_unique(array_merge($ownSniffs, $includedSniffs)); |
914
|
|
|
$excludedSniffs = array_unique($excludedSniffs); |
915
|
|
|
|
916
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
917
|
|
|
$included = count($includedSniffs); |
918
|
|
|
$excluded = count($excludedSniffs); |
919
|
|
|
echo str_repeat("\t", $depth); |
920
|
|
|
echo "=> Ruleset processing complete; included $included sniffs and excluded $excluded".PHP_EOL; |
921
|
|
|
} |
922
|
|
|
|
923
|
|
|
// Merge our own sniff list with our externally included |
924
|
|
|
// sniff list, but filter out any excluded sniffs. |
925
|
|
|
$files = array(); |
926
|
|
|
foreach ($includedSniffs as $sniff) { |
927
|
|
|
if (in_array($sniff, $excludedSniffs) === true) { |
928
|
|
|
continue; |
929
|
|
|
} else { |
930
|
|
|
$files[] = self::realpath($sniff); |
931
|
|
|
} |
932
|
|
|
} |
933
|
|
|
|
934
|
|
|
return $files; |
935
|
|
|
|
936
|
|
|
}//end processRuleset() |
937
|
|
|
|
938
|
|
|
|
939
|
|
|
/** |
940
|
|
|
* Expands a directory into a list of sniff files within. |
941
|
|
|
* |
942
|
|
|
* @param string $directory The path to a directory. |
943
|
|
|
* @param int $depth How many nested processing steps we are in. This |
944
|
|
|
* is only used for debug output. |
945
|
|
|
* |
946
|
|
|
* @return array |
947
|
|
|
*/ |
948
|
|
|
private function _expandSniffDirectory($directory, $depth=0) |
949
|
|
|
{ |
950
|
|
|
$sniffs = array(); |
951
|
|
|
|
952
|
|
|
if (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') === true) { |
953
|
|
|
$rdi = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::FOLLOW_SYMLINKS); |
954
|
|
|
} else { |
955
|
|
|
$rdi = new RecursiveDirectoryIterator($directory); |
956
|
|
|
} |
957
|
|
|
|
958
|
|
|
$di = new RecursiveIteratorIterator($rdi, 0, RecursiveIteratorIterator::CATCH_GET_CHILD); |
959
|
|
|
|
960
|
|
|
$dirLen = strlen($directory); |
961
|
|
|
|
962
|
|
|
foreach ($di as $file) { |
963
|
|
|
$filename = $file->getFilename(); |
964
|
|
|
|
965
|
|
|
// Skip hidden files. |
966
|
|
|
if (substr($filename, 0, 1) === '.') { |
967
|
|
|
continue; |
968
|
|
|
} |
969
|
|
|
|
970
|
|
|
// We are only interested in PHP and sniff files. |
971
|
|
|
$fileParts = explode('.', $filename); |
972
|
|
|
if (array_pop($fileParts) !== 'php') { |
973
|
|
|
continue; |
974
|
|
|
} |
975
|
|
|
|
976
|
|
|
$basename = basename($filename, '.php'); |
977
|
|
|
if (substr($basename, -5) !== 'Sniff') { |
978
|
|
|
continue; |
979
|
|
|
} |
980
|
|
|
|
981
|
|
|
$path = $file->getPathname(); |
982
|
|
|
|
983
|
|
|
// Skip files in hidden directories within the Sniffs directory of this |
984
|
|
|
// standard. We use the offset with strpos() to allow hidden directories |
985
|
|
|
// before, valid example: |
986
|
|
|
// /home/foo/.composer/vendor/drupal/coder/coder_sniffer/Drupal/Sniffs/... |
987
|
|
|
if (strpos($path, DIRECTORY_SEPARATOR.'.', $dirLen) !== false) { |
988
|
|
|
continue; |
989
|
|
|
} |
990
|
|
|
|
991
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
992
|
|
|
echo str_repeat("\t", $depth); |
993
|
|
|
echo "\t\t=> $path".PHP_EOL; |
994
|
|
|
} |
995
|
|
|
|
996
|
|
|
$sniffs[] = $path; |
997
|
|
|
}//end foreach |
998
|
|
|
|
999
|
|
|
return $sniffs; |
1000
|
|
|
|
1001
|
|
|
}//end _expandSniffDirectory() |
1002
|
|
|
|
1003
|
|
|
|
1004
|
|
|
/** |
1005
|
|
|
* Expands a ruleset reference into a list of sniff files. |
1006
|
|
|
* |
1007
|
|
|
* @param string $ref The reference from the ruleset XML file. |
1008
|
|
|
* @param string $rulesetDir The directory of the ruleset XML file, used to |
1009
|
|
|
* evaluate relative paths. |
1010
|
|
|
* @param int $depth How many nested processing steps we are in. This |
1011
|
|
|
* is only used for debug output. |
1012
|
|
|
* |
1013
|
|
|
* @return array |
1014
|
|
|
* @throws PHP_CodeSniffer_Exception If the reference is invalid. |
1015
|
|
|
*/ |
1016
|
|
|
private function _expandRulesetReference($ref, $rulesetDir, $depth=0) |
1017
|
|
|
{ |
1018
|
|
|
// Ignore internal sniffs codes as they are used to only |
1019
|
|
|
// hide and change internal messages. |
1020
|
|
View Code Duplication |
if (substr($ref, 0, 9) === 'Internal.') { |
|
|
|
|
1021
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1022
|
|
|
echo str_repeat("\t", $depth); |
1023
|
|
|
echo "\t\t* ignoring internal sniff code *".PHP_EOL; |
1024
|
|
|
} |
1025
|
|
|
|
1026
|
|
|
return array(); |
1027
|
|
|
} |
1028
|
|
|
|
1029
|
|
|
// As sniffs can't begin with a full stop, assume references in |
1030
|
|
|
// this format are relative paths and attempt to convert them |
1031
|
|
|
// to absolute paths. If this fails, let the reference run through |
1032
|
|
|
// the normal checks and have it fail as normal. |
1033
|
|
View Code Duplication |
if (substr($ref, 0, 1) === '.') { |
|
|
|
|
1034
|
|
|
$realpath = self::realpath($rulesetDir.'/'.$ref); |
1035
|
|
|
if ($realpath !== false) { |
1036
|
|
|
$ref = $realpath; |
1037
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1038
|
|
|
echo str_repeat("\t", $depth); |
1039
|
|
|
echo "\t\t=> $ref".PHP_EOL; |
1040
|
|
|
} |
1041
|
|
|
} |
1042
|
|
|
} |
1043
|
|
|
|
1044
|
|
|
// As sniffs can't begin with a tilde, assume references in |
1045
|
|
|
// this format at relative to the user's home directory. |
1046
|
|
View Code Duplication |
if (substr($ref, 0, 2) === '~/') { |
|
|
|
|
1047
|
|
|
$realpath = self::realpath($ref); |
1048
|
|
|
if ($realpath !== false) { |
1049
|
|
|
$ref = $realpath; |
1050
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1051
|
|
|
echo str_repeat("\t", $depth); |
1052
|
|
|
echo "\t\t=> $ref".PHP_EOL; |
1053
|
|
|
} |
1054
|
|
|
} |
1055
|
|
|
} |
1056
|
|
|
|
1057
|
|
|
if (is_file($ref) === true) { |
1058
|
|
|
if (substr($ref, -9) === 'Sniff.php') { |
1059
|
|
|
// A single external sniff. |
1060
|
|
|
self::$rulesetDirs[] = dirname(dirname(dirname($ref))); |
1061
|
|
|
return array($ref); |
1062
|
|
|
} |
1063
|
|
|
} else { |
1064
|
|
|
// See if this is a whole standard being referenced. |
1065
|
|
|
$path = $this->getInstalledStandardPath($ref); |
1066
|
|
|
if (self::isPharFile($path) === true && strpos($path, 'ruleset.xml') === false) { |
|
|
|
|
1067
|
|
|
// If the ruleset exists inside the phar file, use it. |
1068
|
|
|
if (file_exists($path.DIRECTORY_SEPARATOR.'ruleset.xml') === true) { |
1069
|
|
|
$path = $path.DIRECTORY_SEPARATOR.'ruleset.xml'; |
1070
|
|
|
} else { |
1071
|
|
|
$path = null; |
1072
|
|
|
} |
1073
|
|
|
} |
1074
|
|
|
|
1075
|
|
|
if ($path !== null) { |
1076
|
|
|
$ref = $path; |
1077
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1078
|
|
|
echo str_repeat("\t", $depth); |
1079
|
|
|
echo "\t\t=> $ref".PHP_EOL; |
1080
|
|
|
} |
1081
|
|
|
} else if (is_dir($ref) === false) { |
1082
|
|
|
// Work out the sniff path. |
1083
|
|
|
$sepPos = strpos($ref, DIRECTORY_SEPARATOR); |
1084
|
|
|
if ($sepPos !== false) { |
1085
|
|
|
$stdName = substr($ref, 0, $sepPos); |
1086
|
|
|
$path = substr($ref, $sepPos); |
1087
|
|
|
} else { |
1088
|
|
|
$parts = explode('.', $ref); |
1089
|
|
|
$stdName = $parts[0]; |
1090
|
|
|
if (count($parts) === 1) { |
1091
|
|
|
// A whole standard? |
1092
|
|
|
$path = ''; |
1093
|
|
|
} else if (count($parts) === 2) { |
1094
|
|
|
// A directory of sniffs? |
1095
|
|
|
$path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1]; |
1096
|
|
|
} else { |
1097
|
|
|
// A single sniff? |
1098
|
|
|
$path = DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$parts[1].DIRECTORY_SEPARATOR.$parts[2].'Sniff.php'; |
1099
|
|
|
} |
1100
|
|
|
} |
1101
|
|
|
|
1102
|
|
|
$newRef = false; |
1103
|
|
|
$stdPath = $this->getInstalledStandardPath($stdName); |
1104
|
|
|
if ($stdPath !== null && $path !== '') { |
1105
|
|
|
if (self::isPharFile($stdPath) === true |
|
|
|
|
1106
|
|
|
&& strpos($stdPath, 'ruleset.xml') === false |
1107
|
|
|
) { |
1108
|
|
|
// Phar files can only return the directory, |
1109
|
|
|
// since ruleset can be omitted if building one standard. |
1110
|
|
|
$newRef = self::realpath($stdPath.$path); |
1111
|
|
|
} else { |
1112
|
|
|
$newRef = self::realpath(dirname($stdPath).$path); |
1113
|
|
|
} |
1114
|
|
|
} |
1115
|
|
|
|
1116
|
|
|
if ($newRef === false) { |
1117
|
|
|
// The sniff is not locally installed, so check if it is being |
1118
|
|
|
// referenced as a remote sniff outside the install. We do this |
1119
|
|
|
// by looking through all directories where we have found ruleset |
1120
|
|
|
// files before, looking for ones for this particular standard, |
1121
|
|
|
// and seeing if it is in there. |
1122
|
|
|
foreach (self::$rulesetDirs as $dir) { |
|
|
|
|
1123
|
|
|
if (strtolower(basename($dir)) !== strtolower($stdName)) { |
1124
|
|
|
continue; |
1125
|
|
|
} |
1126
|
|
|
|
1127
|
|
|
$newRef = self::realpath($dir.$path); |
1128
|
|
|
|
1129
|
|
|
if ($newRef !== false) { |
1130
|
|
|
$ref = $newRef; |
1131
|
|
|
} |
1132
|
|
|
} |
1133
|
|
|
} else { |
1134
|
|
|
$ref = $newRef; |
1135
|
|
|
} |
1136
|
|
|
|
1137
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1138
|
|
|
echo str_repeat("\t", $depth); |
1139
|
|
|
echo "\t\t=> $ref".PHP_EOL; |
1140
|
|
|
} |
1141
|
|
|
}//end if |
1142
|
|
|
}//end if |
1143
|
|
|
|
1144
|
|
|
if (is_dir($ref) === true) { |
1145
|
|
|
if (is_file($ref.DIRECTORY_SEPARATOR.'ruleset.xml') === true) { |
1146
|
|
|
// We are referencing an external coding standard. |
1147
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1148
|
|
|
echo str_repeat("\t", $depth); |
1149
|
|
|
echo "\t\t* rule is referencing a standard using directory name; processing *".PHP_EOL; |
1150
|
|
|
} |
1151
|
|
|
|
1152
|
|
|
return $this->processRuleset($ref.DIRECTORY_SEPARATOR.'ruleset.xml', ($depth + 2)); |
1153
|
|
|
} else { |
1154
|
|
|
// We are referencing a whole directory of sniffs. |
1155
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1156
|
|
|
echo str_repeat("\t", $depth); |
1157
|
|
|
echo "\t\t* rule is referencing a directory of sniffs *".PHP_EOL; |
1158
|
|
|
echo str_repeat("\t", $depth); |
1159
|
|
|
echo "\t\tAdding sniff files from directory".PHP_EOL; |
1160
|
|
|
} |
1161
|
|
|
|
1162
|
|
|
return $this->_expandSniffDirectory($ref, ($depth + 1)); |
|
|
|
|
1163
|
|
|
} |
1164
|
|
|
} else { |
1165
|
|
|
if (is_file($ref) === false) { |
1166
|
|
|
$error = "Referenced sniff \"$ref\" does not exist"; |
1167
|
|
|
throw new PHP_CodeSniffer_Exception($error); |
1168
|
|
|
} |
1169
|
|
|
|
1170
|
|
|
if (substr($ref, -9) === 'Sniff.php') { |
1171
|
|
|
// A single sniff. |
1172
|
|
|
return array($ref); |
1173
|
|
|
} else { |
1174
|
|
|
// Assume an external ruleset.xml file. |
1175
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1176
|
|
|
echo str_repeat("\t", $depth); |
1177
|
|
|
echo "\t\t* rule is referencing a standard using ruleset path; processing *".PHP_EOL; |
1178
|
|
|
} |
1179
|
|
|
|
1180
|
|
|
return $this->processRuleset($ref, ($depth + 2)); |
|
|
|
|
1181
|
|
|
} |
1182
|
|
|
}//end if |
1183
|
|
|
|
1184
|
|
|
}//end _expandRulesetReference() |
1185
|
|
|
|
1186
|
|
|
|
1187
|
|
|
/** |
1188
|
|
|
* Processes a rule from a ruleset XML file, overriding built-in defaults. |
1189
|
|
|
* |
1190
|
|
|
* @param SimpleXMLElement $rule The rule object from a ruleset XML file. |
1191
|
|
|
* @param int $depth How many nested processing steps we are in. |
1192
|
|
|
* This is only used for debug output. |
1193
|
|
|
* |
1194
|
|
|
* @return void |
1195
|
|
|
*/ |
1196
|
|
|
private function _processRule($rule, $depth=0) |
1197
|
|
|
{ |
1198
|
|
|
$code = (string) $rule['ref']; |
1199
|
|
|
|
1200
|
|
|
// Custom severity. |
1201
|
|
View Code Duplication |
if (isset($rule->severity) === true |
|
|
|
|
1202
|
|
|
&& $this->_shouldProcessElement($rule->severity) === true |
1203
|
|
|
) { |
1204
|
|
|
if (isset($this->ruleset[$code]) === false) { |
1205
|
|
|
$this->ruleset[$code] = array(); |
1206
|
|
|
} |
1207
|
|
|
|
1208
|
|
|
$this->ruleset[$code]['severity'] = (int) $rule->severity; |
1209
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1210
|
|
|
echo str_repeat("\t", $depth); |
1211
|
|
|
echo "\t\t=> severity set to ".(int) $rule->severity.PHP_EOL; |
1212
|
|
|
} |
1213
|
|
|
} |
1214
|
|
|
|
1215
|
|
|
// Custom message type. |
1216
|
|
View Code Duplication |
if (isset($rule->type) === true |
|
|
|
|
1217
|
|
|
&& $this->_shouldProcessElement($rule->type) === true |
1218
|
|
|
) { |
1219
|
|
|
if (isset($this->ruleset[$code]) === false) { |
1220
|
|
|
$this->ruleset[$code] = array(); |
1221
|
|
|
} |
1222
|
|
|
|
1223
|
|
|
$this->ruleset[$code]['type'] = (string) $rule->type; |
1224
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1225
|
|
|
echo str_repeat("\t", $depth); |
1226
|
|
|
echo "\t\t=> message type set to ".(string) $rule->type.PHP_EOL; |
1227
|
|
|
} |
1228
|
|
|
} |
1229
|
|
|
|
1230
|
|
|
// Custom message. |
1231
|
|
View Code Duplication |
if (isset($rule->message) === true |
|
|
|
|
1232
|
|
|
&& $this->_shouldProcessElement($rule->message) === true |
1233
|
|
|
) { |
1234
|
|
|
if (isset($this->ruleset[$code]) === false) { |
1235
|
|
|
$this->ruleset[$code] = array(); |
1236
|
|
|
} |
1237
|
|
|
|
1238
|
|
|
$this->ruleset[$code]['message'] = (string) $rule->message; |
1239
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1240
|
|
|
echo str_repeat("\t", $depth); |
1241
|
|
|
echo "\t\t=> message set to ".(string) $rule->message.PHP_EOL; |
1242
|
|
|
} |
1243
|
|
|
} |
1244
|
|
|
|
1245
|
|
|
// Custom properties. |
1246
|
|
|
if (isset($rule->properties) === true |
1247
|
|
|
&& $this->_shouldProcessElement($rule->properties) === true |
1248
|
|
|
) { |
1249
|
|
|
foreach ($rule->properties->property as $prop) { |
1250
|
|
|
if ($this->_shouldProcessElement($prop) === false) { |
1251
|
|
|
continue; |
1252
|
|
|
} |
1253
|
|
|
|
1254
|
|
|
if (isset($this->ruleset[$code]) === false) { |
1255
|
|
|
$this->ruleset[$code] = array( |
1256
|
|
|
'properties' => array(), |
1257
|
|
|
); |
1258
|
|
|
} else if (isset($this->ruleset[$code]['properties']) === false) { |
1259
|
|
|
$this->ruleset[$code]['properties'] = array(); |
1260
|
|
|
} |
1261
|
|
|
|
1262
|
|
|
$name = (string) $prop['name']; |
1263
|
|
|
if (isset($prop['type']) === true |
1264
|
|
|
&& (string) $prop['type'] === 'array' |
1265
|
|
|
) { |
1266
|
|
|
$value = (string) $prop['value']; |
1267
|
|
|
$values = array(); |
1268
|
|
|
foreach (explode(',', $value) as $val) { |
1269
|
|
|
$v = ''; |
1270
|
|
|
|
1271
|
|
|
list($k,$v) = explode('=>', $val.'=>'); |
1272
|
|
|
if ($v !== '') { |
1273
|
|
|
$values[$k] = $v; |
1274
|
|
|
} else { |
1275
|
|
|
$values[] = $k; |
1276
|
|
|
} |
1277
|
|
|
} |
1278
|
|
|
|
1279
|
|
|
$this->ruleset[$code]['properties'][$name] = $values; |
1280
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1281
|
|
|
echo str_repeat("\t", $depth); |
1282
|
|
|
echo "\t\t=> array property \"$name\" set to \"$value\"".PHP_EOL; |
1283
|
|
|
} |
1284
|
|
|
} else { |
1285
|
|
|
$this->ruleset[$code]['properties'][$name] = (string) $prop['value']; |
1286
|
|
View Code Duplication |
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
|
|
|
|
1287
|
|
|
echo str_repeat("\t", $depth); |
1288
|
|
|
echo "\t\t=> property \"$name\" set to \"".(string) $prop['value'].'"'.PHP_EOL; |
1289
|
|
|
} |
1290
|
|
|
}//end if |
1291
|
|
|
}//end foreach |
1292
|
|
|
}//end if |
1293
|
|
|
|
1294
|
|
|
// Ignore patterns. |
1295
|
|
|
foreach ($rule->{'exclude-pattern'} as $pattern) { |
1296
|
|
|
if ($this->_shouldProcessElement($pattern) === false) { |
1297
|
|
|
continue; |
1298
|
|
|
} |
1299
|
|
|
|
1300
|
|
|
if (isset($this->ignorePatterns[$code]) === false) { |
1301
|
|
|
$this->ignorePatterns[$code] = array(); |
1302
|
|
|
} |
1303
|
|
|
|
1304
|
|
|
if (isset($pattern['type']) === false) { |
1305
|
|
|
$pattern['type'] = 'absolute'; |
1306
|
|
|
} |
1307
|
|
|
|
1308
|
|
|
$this->ignorePatterns[$code][(string) $pattern] = (string) $pattern['type']; |
1309
|
|
View Code Duplication |
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
|
|
|
|
1310
|
|
|
echo str_repeat("\t", $depth); |
1311
|
|
|
echo "\t\t=> added sniff-specific ".(string) $pattern['type'].' ignore pattern: '.(string) $pattern.PHP_EOL; |
1312
|
|
|
} |
1313
|
|
|
} |
1314
|
|
|
|
1315
|
|
|
}//end _processRule() |
1316
|
|
|
|
1317
|
|
|
|
1318
|
|
|
/** |
1319
|
|
|
* Determine if an element should be processed or ignored. |
1320
|
|
|
* |
1321
|
|
|
* @param SimpleXMLElement $element An object from a ruleset XML file. |
1322
|
|
|
* @param int $depth How many nested processing steps we are in. |
1323
|
|
|
* This is only used for debug output. |
1324
|
|
|
* |
1325
|
|
|
* @return bool |
1326
|
|
|
*/ |
1327
|
|
|
private function _shouldProcessElement($element, $depth=0) |
|
|
|
|
1328
|
|
|
{ |
1329
|
|
|
if (isset($element['phpcbf-only']) === false |
1330
|
|
|
&& isset($element['phpcs-only']) === false |
1331
|
|
|
) { |
1332
|
|
|
// No exceptions are being made. |
1333
|
|
|
return true; |
1334
|
|
|
} |
1335
|
|
|
|
1336
|
|
View Code Duplication |
if (PHP_CODESNIFFER_CBF === true |
|
|
|
|
1337
|
|
|
&& isset($element['phpcbf-only']) === true |
1338
|
|
|
&& (string) $element['phpcbf-only'] === 'true' |
1339
|
|
|
) { |
1340
|
|
|
return true; |
1341
|
|
|
} |
1342
|
|
|
|
1343
|
|
View Code Duplication |
if (PHP_CODESNIFFER_CBF === false |
|
|
|
|
1344
|
|
|
&& isset($element['phpcs-only']) === true |
1345
|
|
|
&& (string) $element['phpcs-only'] === 'true' |
1346
|
|
|
) { |
1347
|
|
|
return true; |
1348
|
|
|
} |
1349
|
|
|
|
1350
|
|
|
return false; |
1351
|
|
|
|
1352
|
|
|
}//end _shouldProcessElement() |
1353
|
|
|
|
1354
|
|
|
|
1355
|
|
|
/** |
1356
|
|
|
* Loads and stores sniffs objects used for sniffing files. |
1357
|
|
|
* |
1358
|
|
|
* @param array $files Paths to the sniff files to register. |
1359
|
|
|
* @param array $restrictions The sniff class names to restrict the allowed |
1360
|
|
|
* listeners to. |
1361
|
|
|
* @param array $exclusions The sniff class names to exclude from the |
1362
|
|
|
* listeners list. |
1363
|
|
|
* |
1364
|
|
|
* @return void |
1365
|
|
|
* @throws PHP_CodeSniffer_Exception If a sniff file path is invalid. |
1366
|
|
|
*/ |
1367
|
|
|
public function registerSniffs($files, $restrictions, $exclusions) |
1368
|
|
|
{ |
1369
|
|
|
$listeners = array(); |
1370
|
|
|
|
1371
|
|
|
foreach ($files as $file) { |
1372
|
|
|
// Work out where the position of /StandardName/Sniffs/... is |
1373
|
|
|
// so we can determine what the class will be called. |
1374
|
|
|
$sniffPos = strrpos($file, DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR); |
1375
|
|
|
if ($sniffPos === false) { |
1376
|
|
|
continue; |
1377
|
|
|
} |
1378
|
|
|
|
1379
|
|
|
$slashPos = strrpos(substr($file, 0, $sniffPos), DIRECTORY_SEPARATOR); |
1380
|
|
|
if ($slashPos === false) { |
1381
|
|
|
continue; |
1382
|
|
|
} |
1383
|
|
|
|
1384
|
|
|
$className = substr($file, ($slashPos + 1)); |
1385
|
|
|
|
1386
|
|
|
if (substr_count($className, DIRECTORY_SEPARATOR) !== 3) { |
1387
|
|
|
throw new PHP_CodeSniffer_Exception("Sniff file $className is not valid; sniff files must be located in a .../StandardName/Sniffs/CategoryName/ directory"); |
1388
|
|
|
} |
1389
|
|
|
|
1390
|
|
|
$className = substr($className, 0, -4); |
1391
|
|
|
$className = str_replace(DIRECTORY_SEPARATOR, '_', $className); |
1392
|
|
|
|
1393
|
|
|
// If they have specified a list of sniffs to restrict to, check |
1394
|
|
|
// to see if this sniff is allowed. |
1395
|
|
View Code Duplication |
if (empty($restrictions) === false |
|
|
|
|
1396
|
|
|
&& in_array(strtolower($className), $restrictions) === false |
1397
|
|
|
) { |
1398
|
|
|
continue; |
1399
|
|
|
} |
1400
|
|
|
|
1401
|
|
|
// If they have specified a list of sniffs to exclude, check |
1402
|
|
|
// to see if this sniff is allowed. |
1403
|
|
View Code Duplication |
if (empty($exclusions) === false |
|
|
|
|
1404
|
|
|
&& in_array(strtolower($className), $exclusions) === true |
1405
|
|
|
) { |
1406
|
|
|
continue; |
1407
|
|
|
} |
1408
|
|
|
|
1409
|
|
|
include_once $file; |
1410
|
|
|
|
1411
|
|
|
// Support the use of PHP namespaces. If the class name we included |
1412
|
|
|
// contains namespace separators instead of underscores, use this as the |
1413
|
|
|
// class name from now on. |
1414
|
|
|
$classNameNS = str_replace('_', '\\', $className); |
1415
|
|
|
if (class_exists($classNameNS, false) === true) { |
1416
|
|
|
$className = $classNameNS; |
1417
|
|
|
} |
1418
|
|
|
|
1419
|
|
|
// Skip abstract classes. |
1420
|
|
|
$reflection = new ReflectionClass($className); |
1421
|
|
|
if ($reflection->isAbstract() === true) { |
1422
|
|
|
continue; |
1423
|
|
|
} |
1424
|
|
|
|
1425
|
|
|
$listeners[$className] = $className; |
1426
|
|
|
|
1427
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 2) { |
1428
|
|
|
echo "Registered $className".PHP_EOL; |
1429
|
|
|
} |
1430
|
|
|
}//end foreach |
1431
|
|
|
|
1432
|
|
|
$this->sniffs = $listeners; |
1433
|
|
|
|
1434
|
|
|
}//end registerSniffs() |
1435
|
|
|
|
1436
|
|
|
|
1437
|
|
|
/** |
1438
|
|
|
* Populates the array of PHP_CodeSniffer_Sniff's for this file. |
1439
|
|
|
* |
1440
|
|
|
* @return void |
1441
|
|
|
* @throws PHP_CodeSniffer_Exception If sniff registration fails. |
1442
|
|
|
*/ |
1443
|
|
|
public function populateTokenListeners() |
1444
|
|
|
{ |
1445
|
|
|
// Construct a list of listeners indexed by token being listened for. |
1446
|
|
|
$this->_tokenListeners = array(); |
1447
|
|
|
|
1448
|
|
|
foreach ($this->sniffs as $listenerClass) { |
1449
|
|
|
// Work out the internal code for this sniff. Detect usage of namespace |
1450
|
|
|
// separators instead of underscores to support PHP namespaces. |
1451
|
|
|
if (strstr($listenerClass, '\\') === false) { |
1452
|
|
|
$parts = explode('_', $listenerClass); |
1453
|
|
|
} else { |
1454
|
|
|
$parts = explode('\\', $listenerClass); |
1455
|
|
|
} |
1456
|
|
|
|
1457
|
|
|
$code = $parts[0].'.'.$parts[2].'.'.$parts[3]; |
1458
|
|
|
$code = substr($code, 0, -5); |
1459
|
|
|
|
1460
|
|
|
$this->listeners[$listenerClass] = new $listenerClass(); |
1461
|
|
|
$this->sniffCodes[$code] = $listenerClass; |
1462
|
|
|
|
1463
|
|
|
// Set custom properties. |
1464
|
|
|
if (isset($this->ruleset[$code]['properties']) === true) { |
1465
|
|
|
foreach ($this->ruleset[$code]['properties'] as $name => $value) { |
1466
|
|
|
$this->setSniffProperty($listenerClass, $name, $value); |
1467
|
|
|
} |
1468
|
|
|
} |
1469
|
|
|
|
1470
|
|
|
$tokenizers = array(); |
1471
|
|
|
$vars = get_class_vars($listenerClass); |
1472
|
|
|
if (isset($vars['supportedTokenizers']) === true) { |
1473
|
|
|
foreach ($vars['supportedTokenizers'] as $tokenizer) { |
1474
|
|
|
$tokenizers[$tokenizer] = $tokenizer; |
1475
|
|
|
} |
1476
|
|
|
} else { |
1477
|
|
|
$tokenizers = array('PHP' => 'PHP'); |
1478
|
|
|
} |
1479
|
|
|
|
1480
|
|
|
$tokens = $this->listeners[$listenerClass]->register(); |
1481
|
|
|
if (is_array($tokens) === false) { |
1482
|
|
|
$msg = "Sniff $listenerClass register() method must return an array"; |
1483
|
|
|
throw new PHP_CodeSniffer_Exception($msg); |
1484
|
|
|
} |
1485
|
|
|
|
1486
|
|
|
$parts = explode('_', str_replace('\\', '_', $listenerClass)); |
1487
|
|
|
$listenerSource = $parts[0].'.'.$parts[2].'.'.substr($parts[3], 0, -5); |
1488
|
|
|
$ignorePatterns = array(); |
1489
|
|
|
$patterns = $this->getIgnorePatterns($listenerSource); |
1490
|
|
|
foreach ($patterns as $pattern => $type) { |
1491
|
|
|
// While there is support for a type of each pattern |
1492
|
|
|
// (absolute or relative) we don't actually support it here. |
1493
|
|
|
$replacements = array( |
1494
|
|
|
'\\,' => ',', |
1495
|
|
|
'*' => '.*', |
1496
|
|
|
); |
1497
|
|
|
|
1498
|
|
|
$ignorePatterns[] = strtr($pattern, $replacements); |
1499
|
|
|
} |
1500
|
|
|
|
1501
|
|
|
foreach ($tokens as $token) { |
1502
|
|
|
if (isset($this->_tokenListeners[$token]) === false) { |
1503
|
|
|
$this->_tokenListeners[$token] = array(); |
1504
|
|
|
} |
1505
|
|
|
|
1506
|
|
|
if (isset($this->_tokenListeners[$token][$listenerClass]) === false) { |
1507
|
|
|
$this->_tokenListeners[$token][$listenerClass] = array( |
1508
|
|
|
'class' => $listenerClass, |
1509
|
|
|
'source' => $listenerSource, |
1510
|
|
|
'tokenizers' => $tokenizers, |
1511
|
|
|
'ignore' => $ignorePatterns, |
1512
|
|
|
); |
1513
|
|
|
} |
1514
|
|
|
} |
1515
|
|
|
}//end foreach |
1516
|
|
|
|
1517
|
|
|
}//end populateTokenListeners() |
1518
|
|
|
|
1519
|
|
|
|
1520
|
|
|
/** |
1521
|
|
|
* Set a single property for a sniff. |
1522
|
|
|
* |
1523
|
|
|
* @param string $listenerClass The class name of the sniff. |
1524
|
|
|
* @param string $name The name of the property to change. |
1525
|
|
|
* @param string $value The new value of the property. |
1526
|
|
|
* |
1527
|
|
|
* @return void |
1528
|
|
|
*/ |
1529
|
|
|
public function setSniffProperty($listenerClass, $name, $value) |
1530
|
|
|
{ |
1531
|
|
|
// Setting a property for a sniff we are not using. |
1532
|
|
|
if (isset($this->listeners[$listenerClass]) === false) { |
1533
|
|
|
return; |
1534
|
|
|
} |
1535
|
|
|
|
1536
|
|
|
$name = trim($name); |
1537
|
|
|
if (is_string($value) === true) { |
1538
|
|
|
$value = trim($value); |
1539
|
|
|
} |
1540
|
|
|
|
1541
|
|
|
// Special case for booleans. |
1542
|
|
|
if ($value === 'true') { |
1543
|
|
|
$value = true; |
1544
|
|
|
} else if ($value === 'false') { |
1545
|
|
|
$value = false; |
1546
|
|
|
} |
1547
|
|
|
|
1548
|
|
|
$this->listeners[$listenerClass]->$name = $value; |
1549
|
|
|
|
1550
|
|
|
}//end setSniffProperty() |
1551
|
|
|
|
1552
|
|
|
|
1553
|
|
|
/** |
1554
|
|
|
* Get a list of files that will be processed. |
1555
|
|
|
* |
1556
|
|
|
* If passed directories, this method will find all files within them. |
1557
|
|
|
* The method will also perform file extension and ignore pattern filtering. |
1558
|
|
|
* |
1559
|
|
|
* @param string $paths A list of file or directory paths to process. |
1560
|
|
|
* @param boolean $local If true, only process 1 level of files in directories |
1561
|
|
|
* |
1562
|
|
|
* @return array |
1563
|
|
|
* @throws Exception If there was an error opening a directory. |
1564
|
|
|
* @see shouldProcessFile() |
1565
|
|
|
*/ |
1566
|
|
|
public function getFilesToProcess($paths, $local=false) |
1567
|
|
|
{ |
1568
|
|
|
$files = array(); |
1569
|
|
|
|
1570
|
|
|
foreach ($paths as $path) { |
|
|
|
|
1571
|
|
|
if (is_dir($path) === true || self::isPharFile($path) === true) { |
1572
|
|
|
if (self::isPharFile($path) === true) { |
1573
|
|
|
$path = 'phar://'.$path; |
1574
|
|
|
} |
1575
|
|
|
|
1576
|
|
|
if ($local === true) { |
1577
|
|
|
$di = new DirectoryIterator($path); |
1578
|
|
|
} else { |
1579
|
|
|
$di = new RecursiveIteratorIterator( |
1580
|
|
|
new RecursiveDirectoryIterator($path), |
1581
|
|
|
0, |
1582
|
|
|
RecursiveIteratorIterator::CATCH_GET_CHILD |
1583
|
|
|
); |
1584
|
|
|
} |
1585
|
|
|
|
1586
|
|
|
foreach ($di as $file) { |
1587
|
|
|
// Check if the file exists after all symlinks are resolved. |
1588
|
|
|
$filePath = self::realpath($file->getPathname()); |
1589
|
|
|
if ($filePath === false) { |
1590
|
|
|
continue; |
1591
|
|
|
} |
1592
|
|
|
|
1593
|
|
|
if (is_dir($filePath) === true) { |
1594
|
|
|
continue; |
1595
|
|
|
} |
1596
|
|
|
|
1597
|
|
|
if ($this->shouldProcessFile($file->getPathname(), $path) === false) { |
1598
|
|
|
continue; |
1599
|
|
|
} |
1600
|
|
|
|
1601
|
|
|
$files[] = $file->getPathname(); |
1602
|
|
|
}//end foreach |
1603
|
|
|
} else { |
1604
|
|
|
if ($this->shouldIgnoreFile($path, dirname($path)) === true) { |
1605
|
|
|
continue; |
1606
|
|
|
} |
1607
|
|
|
|
1608
|
|
|
$files[] = $path; |
1609
|
|
|
}//end if |
1610
|
|
|
}//end foreach |
1611
|
|
|
|
1612
|
|
|
return $files; |
1613
|
|
|
|
1614
|
|
|
}//end getFilesToProcess() |
1615
|
|
|
|
1616
|
|
|
|
1617
|
|
|
/** |
1618
|
|
|
* Checks filtering rules to see if a file should be checked. |
1619
|
|
|
* |
1620
|
|
|
* Checks both file extension filters and path ignore filters. |
1621
|
|
|
* |
1622
|
|
|
* @param string $path The path to the file being checked. |
1623
|
|
|
* @param string $basedir The directory to use for relative path checks. |
1624
|
|
|
* |
1625
|
|
|
* @return bool |
1626
|
|
|
*/ |
1627
|
|
|
public function shouldProcessFile($path, $basedir) |
1628
|
|
|
{ |
1629
|
|
|
// Check that the file's extension is one we are checking. |
1630
|
|
|
// We are strict about checking the extension and we don't |
1631
|
|
|
// let files through with no extension or that start with a dot. |
1632
|
|
|
$fileName = basename($path); |
1633
|
|
|
$fileParts = explode('.', $fileName); |
1634
|
|
|
if ($fileParts[0] === $fileName || $fileParts[0] === '') { |
1635
|
|
|
return false; |
1636
|
|
|
} |
1637
|
|
|
|
1638
|
|
|
// Checking multi-part file extensions, so need to create a |
1639
|
|
|
// complete extension list and make sure one is allowed. |
1640
|
|
|
$extensions = array(); |
1641
|
|
|
array_shift($fileParts); |
1642
|
|
|
foreach ($fileParts as $part) { |
1643
|
|
|
$extensions[implode('.', $fileParts)] = 1; |
1644
|
|
|
array_shift($fileParts); |
1645
|
|
|
} |
1646
|
|
|
|
1647
|
|
|
$matches = array_intersect_key($extensions, $this->allowedFileExtensions); |
1648
|
|
|
if (empty($matches) === true) { |
1649
|
|
|
return false; |
1650
|
|
|
} |
1651
|
|
|
|
1652
|
|
|
// If the file's path matches one of our ignore patterns, skip it. |
1653
|
|
|
if ($this->shouldIgnoreFile($path, $basedir) === true) { |
1654
|
|
|
return false; |
1655
|
|
|
} |
1656
|
|
|
|
1657
|
|
|
return true; |
1658
|
|
|
|
1659
|
|
|
}//end shouldProcessFile() |
1660
|
|
|
|
1661
|
|
|
|
1662
|
|
|
/** |
1663
|
|
|
* Checks filtering rules to see if a file should be ignored. |
1664
|
|
|
* |
1665
|
|
|
* @param string $path The path to the file being checked. |
1666
|
|
|
* @param string $basedir The directory to use for relative path checks. |
1667
|
|
|
* |
1668
|
|
|
* @return bool |
1669
|
|
|
*/ |
1670
|
|
|
public function shouldIgnoreFile($path, $basedir) |
1671
|
|
|
{ |
1672
|
|
|
$relativePath = $path; |
1673
|
|
|
if (strpos($path, $basedir) === 0) { |
1674
|
|
|
// The +1 cuts off the directory separator as well. |
1675
|
|
|
$relativePath = substr($path, (strlen($basedir) + 1)); |
1676
|
|
|
} |
1677
|
|
|
|
1678
|
|
|
foreach ($this->ignorePatterns as $pattern => $type) { |
1679
|
|
|
if (is_array($type) === true) { |
1680
|
|
|
// A sniff specific ignore pattern. |
1681
|
|
|
continue; |
1682
|
|
|
} |
1683
|
|
|
|
1684
|
|
|
// Maintains backwards compatibility in case the ignore pattern does |
1685
|
|
|
// not have a relative/absolute value. |
1686
|
|
|
if (is_int($pattern) === true) { |
1687
|
|
|
$pattern = $type; |
1688
|
|
|
$type = 'absolute'; |
1689
|
|
|
} |
1690
|
|
|
|
1691
|
|
|
$replacements = array( |
1692
|
|
|
'\\,' => ',', |
1693
|
|
|
'*' => '.*', |
1694
|
|
|
); |
1695
|
|
|
|
1696
|
|
|
// We assume a / directory separator, as do the exclude rules |
1697
|
|
|
// most developers write, so we need a special case for any system |
1698
|
|
|
// that is different. |
1699
|
|
|
if (DIRECTORY_SEPARATOR === '\\') { |
1700
|
|
|
$replacements['/'] = '\\\\'; |
1701
|
|
|
} |
1702
|
|
|
|
1703
|
|
|
$pattern = strtr($pattern, $replacements); |
1704
|
|
|
|
1705
|
|
|
if ($type === 'relative') { |
1706
|
|
|
$testPath = $relativePath; |
1707
|
|
|
} else { |
1708
|
|
|
$testPath = $path; |
1709
|
|
|
} |
1710
|
|
|
|
1711
|
|
|
$pattern = '`'.$pattern.'`i'; |
1712
|
|
|
if (preg_match($pattern, $testPath) === 1) { |
1713
|
|
|
return true; |
1714
|
|
|
} |
1715
|
|
|
}//end foreach |
1716
|
|
|
|
1717
|
|
|
return false; |
1718
|
|
|
|
1719
|
|
|
}//end shouldIgnoreFile() |
1720
|
|
|
|
1721
|
|
|
|
1722
|
|
|
/** |
1723
|
|
|
* Run the code sniffs over a single given file. |
1724
|
|
|
* |
1725
|
|
|
* Processes the file and runs the PHP_CodeSniffer sniffs to verify that it |
1726
|
|
|
* conforms with the standard. Returns the processed file object, or NULL |
1727
|
|
|
* if no file was processed due to error. |
1728
|
|
|
* |
1729
|
|
|
* @param string $file The file to process. |
1730
|
|
|
* @param string $contents The contents to parse. If NULL, the content |
1731
|
|
|
* is taken from the file system. |
1732
|
|
|
* |
1733
|
|
|
* @return PHP_CodeSniffer_File |
1734
|
|
|
* @throws PHP_CodeSniffer_Exception If the file could not be processed. |
1735
|
|
|
* @see _processFile() |
1736
|
|
|
*/ |
1737
|
|
|
public function processFile($file, $contents=null) |
1738
|
|
|
{ |
1739
|
|
|
if ($contents === null && file_exists($file) === false) { |
1740
|
|
|
throw new PHP_CodeSniffer_Exception("Source file $file does not exist"); |
1741
|
|
|
} |
1742
|
|
|
|
1743
|
|
|
$filePath = self::realpath($file); |
1744
|
|
|
if ($filePath === false) { |
1745
|
|
|
$filePath = $file; |
1746
|
|
|
} |
1747
|
|
|
|
1748
|
|
|
// Before we go and spend time tokenizing this file, just check |
1749
|
|
|
// to see if there is a tag up top to indicate that the whole |
1750
|
|
|
// file should be ignored. It must be on one of the first two lines. |
1751
|
|
|
$firstContent = $contents; |
|
|
|
|
1752
|
|
|
if ($contents === null && is_readable($filePath) === true) { |
1753
|
|
|
$handle = fopen($filePath, 'r'); |
1754
|
|
|
stream_set_blocking($handle, true); |
1755
|
|
|
if ($handle !== false) { |
1756
|
|
|
$firstContent = fgets($handle); |
1757
|
|
|
$firstContent .= fgets($handle); |
1758
|
|
|
fclose($handle); |
1759
|
|
|
|
1760
|
|
|
if (strpos($firstContent, '@codingStandardsIgnoreFile') !== false) { |
1761
|
|
|
// We are ignoring the whole file. |
1762
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 0) { |
1763
|
|
|
echo 'Ignoring '.basename($filePath).PHP_EOL; |
1764
|
|
|
} |
1765
|
|
|
|
1766
|
|
|
return null; |
1767
|
|
|
} |
1768
|
|
|
} |
1769
|
|
|
}//end if |
1770
|
|
|
|
1771
|
|
|
try { |
1772
|
|
|
$phpcsFile = $this->_processFile($file, $contents); |
1773
|
|
|
} catch (Exception $e) { |
1774
|
|
|
$trace = $e->getTrace(); |
1775
|
|
|
|
1776
|
|
|
$filename = $trace[0]['args'][0]; |
1777
|
|
|
if (is_object($filename) === true |
1778
|
|
|
&& get_class($filename) === 'PHP_CodeSniffer_File' |
1779
|
|
|
) { |
1780
|
|
|
$filename = $filename->getFilename(); |
1781
|
|
|
} else if (is_numeric($filename) === true) { |
1782
|
|
|
// See if we can find the PHP_CodeSniffer_File object. |
1783
|
|
|
foreach ($trace as $data) { |
1784
|
|
|
if (isset($data['args'][0]) === true |
1785
|
|
|
&& ($data['args'][0] instanceof PHP_CodeSniffer_File) === true |
1786
|
|
|
) { |
1787
|
|
|
$filename = $data['args'][0]->getFilename(); |
1788
|
|
|
} |
1789
|
|
|
} |
1790
|
|
|
} else if (is_string($filename) === false) { |
1791
|
|
|
$filename = (string) $filename; |
1792
|
|
|
} |
1793
|
|
|
|
1794
|
|
|
$errorMessage = '"'.$e->getMessage().'" at '.$e->getFile().':'.$e->getLine(); |
1795
|
|
|
$error = "An error occurred during processing; checking has been aborted. The error message was: $errorMessage"; |
1796
|
|
|
|
1797
|
|
|
$phpcsFile = new PHP_CodeSniffer_File( |
1798
|
|
|
$filename, |
1799
|
|
|
$this->_tokenListeners, |
1800
|
|
|
$this->ruleset, |
1801
|
|
|
$this |
1802
|
|
|
); |
1803
|
|
|
|
1804
|
|
|
$phpcsFile->addError($error, null); |
1805
|
|
|
}//end try |
1806
|
|
|
|
1807
|
|
|
$cliValues = $this->cli->getCommandLineValues(); |
1808
|
|
|
|
1809
|
|
|
if (PHP_CODESNIFFER_INTERACTIVE === false) { |
1810
|
|
|
// Cache the report data for this file so we can unset it to save memory. |
1811
|
|
|
$this->reporting->cacheFileReport($phpcsFile, $cliValues); |
1812
|
|
|
$phpcsFile->cleanUp(); |
1813
|
|
|
return $phpcsFile; |
1814
|
|
|
} |
1815
|
|
|
|
1816
|
|
|
/* |
1817
|
|
|
Running interactively. |
1818
|
|
|
Print the error report for the current file and then wait for user input. |
1819
|
|
|
*/ |
1820
|
|
|
|
1821
|
|
|
// Get current violations and then clear the list to make sure |
1822
|
|
|
// we only print violations for a single file each time. |
1823
|
|
|
$numErrors = null; |
1824
|
|
|
while ($numErrors !== 0) { |
1825
|
|
|
$numErrors = ($phpcsFile->getErrorCount() + $phpcsFile->getWarningCount()); |
1826
|
|
|
if ($numErrors === 0) { |
1827
|
|
|
continue; |
1828
|
|
|
} |
1829
|
|
|
|
1830
|
|
|
$reportClass = $this->reporting->factory('full'); |
1831
|
|
|
$reportData = $this->reporting->prepareFileReport($phpcsFile); |
1832
|
|
|
$reportClass->generateFileReport($reportData, $phpcsFile, $cliValues['showSources'], $cliValues['reportWidth']); |
1833
|
|
|
|
1834
|
|
|
echo '<ENTER> to recheck, [s] to skip or [q] to quit : '; |
1835
|
|
|
$input = fgets(STDIN); |
1836
|
|
|
$input = trim($input); |
1837
|
|
|
|
1838
|
|
|
switch ($input) { |
1839
|
|
|
case 's': |
1840
|
|
|
break(2); |
1841
|
|
|
case 'q': |
1842
|
|
|
exit(0); |
|
|
|
|
1843
|
|
|
break; |
|
|
|
|
1844
|
|
|
default: |
1845
|
|
|
// Repopulate the sniffs because some of them save their state |
1846
|
|
|
// and only clear it when the file changes, but we are rechecking |
1847
|
|
|
// the same file. |
1848
|
|
|
$this->populateTokenListeners(); |
1849
|
|
|
$phpcsFile = $this->_processFile($file, $contents); |
1850
|
|
|
break; |
1851
|
|
|
} |
1852
|
|
|
}//end while |
1853
|
|
|
|
1854
|
|
|
return $phpcsFile; |
1855
|
|
|
|
1856
|
|
|
}//end processFile() |
1857
|
|
|
|
1858
|
|
|
|
1859
|
|
|
/** |
1860
|
|
|
* Process the sniffs for a single file. |
1861
|
|
|
* |
1862
|
|
|
* Does raw processing only. No interactive support or error checking. |
1863
|
|
|
* |
1864
|
|
|
* @param string $file The file to process. |
1865
|
|
|
* @param string $contents The contents to parse. If NULL, the content |
1866
|
|
|
* is taken from the file system. |
1867
|
|
|
* |
1868
|
|
|
* @return PHP_CodeSniffer_File |
1869
|
|
|
* @see processFile() |
1870
|
|
|
*/ |
1871
|
|
|
private function _processFile($file, $contents) |
1872
|
|
|
{ |
1873
|
|
|
$stdin = false; |
1874
|
|
|
$cliValues = $this->cli->getCommandLineValues(); |
1875
|
|
|
if (empty($cliValues['files']) === true) { |
1876
|
|
|
$stdin = true; |
1877
|
|
|
} |
1878
|
|
|
|
1879
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 0 || (PHP_CODESNIFFER_CBF === true && $stdin === false)) { |
1880
|
|
|
$startTime = microtime(true); |
1881
|
|
|
echo 'Processing '.basename($file).' '; |
1882
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 1) { |
1883
|
|
|
echo PHP_EOL; |
1884
|
|
|
} |
1885
|
|
|
} |
1886
|
|
|
|
1887
|
|
|
$phpcsFile = new PHP_CodeSniffer_File( |
1888
|
|
|
$file, |
1889
|
|
|
$this->_tokenListeners, |
1890
|
|
|
$this->ruleset, |
1891
|
|
|
$this |
1892
|
|
|
); |
1893
|
|
|
|
1894
|
|
|
$phpcsFile->start($contents); |
1895
|
|
|
|
1896
|
|
|
if (PHP_CODESNIFFER_VERBOSITY > 0 || (PHP_CODESNIFFER_CBF === true && $stdin === false)) { |
1897
|
|
|
$timeTaken = ((microtime(true) - $startTime) * 1000); |
|
|
|
|
1898
|
|
|
if ($timeTaken < 1000) { |
1899
|
|
|
$timeTaken = round($timeTaken); |
1900
|
|
|
echo "DONE in {$timeTaken}ms"; |
1901
|
|
|
} else { |
1902
|
|
|
$timeTaken = round(($timeTaken / 1000), 2); |
1903
|
|
|
echo "DONE in $timeTaken secs"; |
1904
|
|
|
} |
1905
|
|
|
|
1906
|
|
|
if (PHP_CODESNIFFER_CBF === true) { |
1907
|
|
|
$errors = $phpcsFile->getFixableCount(); |
1908
|
|
|
echo " ($errors fixable violations)".PHP_EOL; |
1909
|
|
|
} else { |
1910
|
|
|
$errors = $phpcsFile->getErrorCount(); |
1911
|
|
|
$warnings = $phpcsFile->getWarningCount(); |
1912
|
|
|
echo " ($errors errors, $warnings warnings)".PHP_EOL; |
1913
|
|
|
} |
1914
|
|
|
} |
1915
|
|
|
|
1916
|
|
|
return $phpcsFile; |
1917
|
|
|
|
1918
|
|
|
}//end _processFile() |
1919
|
|
|
|
1920
|
|
|
|
1921
|
|
|
/** |
1922
|
|
|
* Generates documentation for a coding standard. |
1923
|
|
|
* |
1924
|
|
|
* @param string $standard The standard to generate docs for |
1925
|
|
|
* @param array $sniffs A list of sniffs to limit the docs to. |
1926
|
|
|
* @param string $generator The name of the generator class to use. |
1927
|
|
|
* |
1928
|
|
|
* @return void |
1929
|
|
|
*/ |
1930
|
|
|
public function generateDocs($standard, array $sniffs=array(), $generator='Text') |
1931
|
|
|
{ |
1932
|
|
|
if (class_exists('PHP_CodeSniffer_DocGenerators_'.$generator, true) === false) { |
1933
|
|
|
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_DocGenerators_'.$generator.' not found'); |
1934
|
|
|
} |
1935
|
|
|
|
1936
|
|
|
$class = "PHP_CodeSniffer_DocGenerators_$generator"; |
1937
|
|
|
$generator = new $class($standard, $sniffs); |
1938
|
|
|
|
1939
|
|
|
$generator->generate(); |
1940
|
|
|
|
1941
|
|
|
}//end generateDocs() |
1942
|
|
|
|
1943
|
|
|
|
1944
|
|
|
/** |
1945
|
|
|
* Gets the array of PHP_CodeSniffer_Sniff's. |
1946
|
|
|
* |
1947
|
|
|
* @return PHP_CodeSniffer_Sniff[] |
1948
|
|
|
*/ |
1949
|
|
|
public function getSniffs() |
1950
|
|
|
{ |
1951
|
|
|
return $this->listeners; |
1952
|
|
|
|
1953
|
|
|
}//end getSniffs() |
1954
|
|
|
|
1955
|
|
|
|
1956
|
|
|
/** |
1957
|
|
|
* Gets the array of PHP_CodeSniffer_Sniff's indexed by token type. |
1958
|
|
|
* |
1959
|
|
|
* @return array |
1960
|
|
|
*/ |
1961
|
|
|
public function getTokenSniffs() |
1962
|
|
|
{ |
1963
|
|
|
return $this->_tokenListeners; |
1964
|
|
|
|
1965
|
|
|
}//end getTokenSniffs() |
1966
|
|
|
|
1967
|
|
|
|
1968
|
|
|
/** |
1969
|
|
|
* Returns true if the specified string is in the camel caps format. |
1970
|
|
|
* |
1971
|
|
|
* @param string $string The string the verify. |
1972
|
|
|
* @param boolean $classFormat If true, check to see if the string is in the |
1973
|
|
|
* class format. Class format strings must start |
1974
|
|
|
* with a capital letter and contain no |
1975
|
|
|
* underscores. |
1976
|
|
|
* @param boolean $public If true, the first character in the string |
1977
|
|
|
* must be an a-z character. If false, the |
1978
|
|
|
* character must be an underscore. This |
1979
|
|
|
* argument is only applicable if $classFormat |
1980
|
|
|
* is false. |
1981
|
|
|
* @param boolean $strict If true, the string must not have two capital |
1982
|
|
|
* letters next to each other. If false, a |
1983
|
|
|
* relaxed camel caps policy is used to allow |
1984
|
|
|
* for acronyms. |
1985
|
|
|
* |
1986
|
|
|
* @return boolean |
1987
|
|
|
*/ |
1988
|
|
|
public static function isCamelCaps( |
1989
|
|
|
$string, |
1990
|
|
|
$classFormat=false, |
1991
|
|
|
$public=true, |
1992
|
|
|
$strict=true |
1993
|
|
|
) { |
1994
|
|
|
// Check the first character first. |
1995
|
|
|
if ($classFormat === false) { |
1996
|
|
|
$legalFirstChar = ''; |
1997
|
|
|
if ($public === false) { |
1998
|
|
|
$legalFirstChar = '[_]'; |
1999
|
|
|
} |
2000
|
|
|
|
2001
|
|
|
if ($strict === false) { |
2002
|
|
|
// Can either start with a lowercase letter, or multiple uppercase |
2003
|
|
|
// in a row, representing an acronym. |
2004
|
|
|
$legalFirstChar .= '([A-Z]{2,}|[a-z])'; |
2005
|
|
|
} else { |
2006
|
|
|
$legalFirstChar .= '[a-z]'; |
2007
|
|
|
} |
2008
|
|
|
} else { |
2009
|
|
|
$legalFirstChar = '[A-Z]'; |
2010
|
|
|
} |
2011
|
|
|
|
2012
|
|
|
if (preg_match("/^$legalFirstChar/", $string) === 0) { |
2013
|
|
|
return false; |
2014
|
|
|
} |
2015
|
|
|
|
2016
|
|
|
// Check that the name only contains legal characters. |
2017
|
|
|
$legalChars = 'a-zA-Z0-9'; |
2018
|
|
|
if (preg_match("|[^$legalChars]|", substr($string, 1)) > 0) { |
2019
|
|
|
return false; |
2020
|
|
|
} |
2021
|
|
|
|
2022
|
|
|
if ($strict === true) { |
2023
|
|
|
// Check that there are not two capital letters next to each other. |
2024
|
|
|
$length = strlen($string); |
2025
|
|
|
$lastCharWasCaps = $classFormat; |
2026
|
|
|
|
2027
|
|
|
for ($i = 1; $i < $length; $i++) { |
2028
|
|
|
$ascii = ord($string{$i}); |
2029
|
|
|
if ($ascii >= 48 && $ascii <= 57) { |
2030
|
|
|
// The character is a number, so it cant be a capital. |
2031
|
|
|
$isCaps = false; |
2032
|
|
|
} else { |
2033
|
|
|
if (strtoupper($string{$i}) === $string{$i}) { |
2034
|
|
|
$isCaps = true; |
2035
|
|
|
} else { |
2036
|
|
|
$isCaps = false; |
2037
|
|
|
} |
2038
|
|
|
} |
2039
|
|
|
|
2040
|
|
|
if ($isCaps === true && $lastCharWasCaps === true) { |
2041
|
|
|
return false; |
2042
|
|
|
} |
2043
|
|
|
|
2044
|
|
|
$lastCharWasCaps = $isCaps; |
2045
|
|
|
} |
2046
|
|
|
}//end if |
2047
|
|
|
|
2048
|
|
|
return true; |
2049
|
|
|
|
2050
|
|
|
}//end isCamelCaps() |
2051
|
|
|
|
2052
|
|
|
|
2053
|
|
|
/** |
2054
|
|
|
* Returns true if the specified string is in the underscore caps format. |
2055
|
|
|
* |
2056
|
|
|
* @param string $string The string to verify. |
2057
|
|
|
* |
2058
|
|
|
* @return boolean |
2059
|
|
|
*/ |
2060
|
|
|
public static function isUnderscoreName($string) |
2061
|
|
|
{ |
2062
|
|
|
// If there are space in the name, it can't be valid. |
2063
|
|
|
if (strpos($string, ' ') !== false) { |
2064
|
|
|
return false; |
2065
|
|
|
} |
2066
|
|
|
|
2067
|
|
|
$validName = true; |
2068
|
|
|
$nameBits = explode('_', $string); |
2069
|
|
|
|
2070
|
|
|
if (preg_match('|^[A-Z]|', $string) === 0) { |
2071
|
|
|
// Name does not begin with a capital letter. |
2072
|
|
|
$validName = false; |
2073
|
|
|
} else { |
2074
|
|
|
foreach ($nameBits as $bit) { |
2075
|
|
|
if ($bit === '') { |
2076
|
|
|
continue; |
2077
|
|
|
} |
2078
|
|
|
|
2079
|
|
|
if ($bit{0} !== strtoupper($bit{0})) { |
2080
|
|
|
$validName = false; |
2081
|
|
|
break; |
2082
|
|
|
} |
2083
|
|
|
} |
2084
|
|
|
} |
2085
|
|
|
|
2086
|
|
|
return $validName; |
2087
|
|
|
|
2088
|
|
|
}//end isUnderscoreName() |
2089
|
|
|
|
2090
|
|
|
|
2091
|
|
|
/** |
2092
|
|
|
* Returns a valid variable type for param/var tag. |
2093
|
|
|
* |
2094
|
|
|
* If type is not one of the standard type, it must be a custom type. |
2095
|
|
|
* Returns the correct type name suggestion if type name is invalid. |
2096
|
|
|
* |
2097
|
|
|
* @param string $varType The variable type to process. |
2098
|
|
|
* |
2099
|
|
|
* @return string |
2100
|
|
|
*/ |
2101
|
|
|
public static function suggestType($varType) |
2102
|
|
|
{ |
2103
|
|
|
if ($varType === '') { |
2104
|
|
|
return ''; |
2105
|
|
|
} |
2106
|
|
|
|
2107
|
|
|
if (in_array($varType, self::$allowedTypes) === true) { |
2108
|
|
|
return $varType; |
2109
|
|
|
} else { |
2110
|
|
|
$lowerVarType = strtolower($varType); |
2111
|
|
|
switch ($lowerVarType) { |
2112
|
|
|
case 'bool': |
2113
|
|
|
case 'boolean': |
2114
|
|
|
return 'boolean'; |
2115
|
|
|
case 'double': |
2116
|
|
|
case 'real': |
2117
|
|
|
case 'float': |
2118
|
|
|
return 'float'; |
2119
|
|
|
case 'int': |
2120
|
|
|
case 'integer': |
2121
|
|
|
return 'integer'; |
2122
|
|
|
case 'array()': |
2123
|
|
|
case 'array': |
2124
|
|
|
return 'array'; |
2125
|
|
|
}//end switch |
2126
|
|
|
|
2127
|
|
|
if (strpos($lowerVarType, 'array(') !== false) { |
2128
|
|
|
// Valid array declaration: |
2129
|
|
|
// array, array(type), array(type1 => type2). |
|
|
|
|
2130
|
|
|
$matches = array(); |
2131
|
|
|
$pattern = '/^array\(\s*([^\s^=^>]*)(\s*=>\s*(.*))?\s*\)/i'; |
2132
|
|
|
if (preg_match($pattern, $varType, $matches) !== 0) { |
2133
|
|
|
$type1 = ''; |
2134
|
|
|
if (isset($matches[1]) === true) { |
2135
|
|
|
$type1 = $matches[1]; |
2136
|
|
|
} |
2137
|
|
|
|
2138
|
|
|
$type2 = ''; |
2139
|
|
|
if (isset($matches[3]) === true) { |
2140
|
|
|
$type2 = $matches[3]; |
2141
|
|
|
} |
2142
|
|
|
|
2143
|
|
|
$type1 = self::suggestType($type1); |
2144
|
|
|
$type2 = self::suggestType($type2); |
2145
|
|
|
if ($type2 !== '') { |
2146
|
|
|
$type2 = ' => '.$type2; |
2147
|
|
|
} |
2148
|
|
|
|
2149
|
|
|
return "array($type1$type2)"; |
2150
|
|
|
} else { |
2151
|
|
|
return 'array'; |
2152
|
|
|
}//end if |
2153
|
|
|
} else if (in_array($lowerVarType, self::$allowedTypes) === true) { |
2154
|
|
|
// A valid type, but not lower cased. |
2155
|
|
|
return $lowerVarType; |
2156
|
|
|
} else { |
2157
|
|
|
// Must be a custom type name. |
2158
|
|
|
return $varType; |
2159
|
|
|
}//end if |
2160
|
|
|
}//end if |
2161
|
|
|
|
2162
|
|
|
}//end suggestType() |
2163
|
|
|
|
2164
|
|
|
|
2165
|
|
|
/** |
2166
|
|
|
* Prepares token content for output to screen. |
2167
|
|
|
* |
2168
|
|
|
* Replaces invisible characters so they are visible. On non-Windows |
2169
|
|
|
* OSes it will also colour the invisible characters. |
2170
|
|
|
* |
2171
|
|
|
* @param string $content The content to prepare. |
2172
|
|
|
* |
2173
|
|
|
* @return string |
2174
|
|
|
*/ |
2175
|
|
|
public static function prepareForOutput($content) |
2176
|
|
|
{ |
2177
|
|
|
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { |
2178
|
|
|
$content = str_replace("\r", '\r', $content); |
2179
|
|
|
$content = str_replace("\n", '\n', $content); |
2180
|
|
|
$content = str_replace("\t", '\t', $content); |
2181
|
|
|
} else { |
2182
|
|
|
$content = str_replace("\r", "\033[30;1m\\r\033[0m", $content); |
2183
|
|
|
$content = str_replace("\n", "\033[30;1m\\n\033[0m", $content); |
2184
|
|
|
$content = str_replace("\t", "\033[30;1m\\t\033[0m", $content); |
2185
|
|
|
$content = str_replace(' ', "\033[30;1m·\033[0m", $content); |
2186
|
|
|
} |
2187
|
|
|
|
2188
|
|
|
return $content; |
2189
|
|
|
|
2190
|
|
|
}//end prepareForOutput() |
2191
|
|
|
|
2192
|
|
|
|
2193
|
|
|
/** |
2194
|
|
|
* Get a list paths where standards are installed. |
2195
|
|
|
* |
2196
|
|
|
* @return array |
2197
|
|
|
*/ |
2198
|
|
|
public static function getInstalledStandardPaths() |
2199
|
|
|
{ |
2200
|
|
|
$installedPaths = array(dirname(__FILE__).DIRECTORY_SEPARATOR.'CodeSniffer'.DIRECTORY_SEPARATOR.'Standards'); |
2201
|
|
|
$configPaths = PHP_CodeSniffer::getConfigData('installed_paths'); |
2202
|
|
|
if ($configPaths !== null) { |
2203
|
|
|
$installedPaths = array_merge($installedPaths, explode(',', $configPaths)); |
2204
|
|
|
} |
2205
|
|
|
|
2206
|
|
|
$resolvedInstalledPaths = array(); |
2207
|
|
|
foreach ($installedPaths as $installedPath) { |
2208
|
|
|
if (substr($installedPath, 0, 1) === '.') { |
2209
|
|
|
$installedPath = dirname(__FILE__).DIRECTORY_SEPARATOR.$installedPath; |
2210
|
|
|
} |
2211
|
|
|
|
2212
|
|
|
$resolvedInstalledPaths[] = $installedPath; |
2213
|
|
|
} |
2214
|
|
|
|
2215
|
|
|
return $resolvedInstalledPaths; |
2216
|
|
|
|
2217
|
|
|
}//end getInstalledStandardPaths() |
2218
|
|
|
|
2219
|
|
|
|
2220
|
|
|
/** |
2221
|
|
|
* Get a list of all coding standards installed. |
2222
|
|
|
* |
2223
|
|
|
* Coding standards are directories located in the |
2224
|
|
|
* CodeSniffer/Standards directory. Valid coding standards |
2225
|
|
|
* include a Sniffs subdirectory. |
2226
|
|
|
* |
2227
|
|
|
* @param boolean $includeGeneric If true, the special "Generic" |
2228
|
|
|
* coding standard will be included |
2229
|
|
|
* if installed. |
2230
|
|
|
* @param string $standardsDir A specific directory to look for standards |
2231
|
|
|
* in. If not specified, PHP_CodeSniffer will |
2232
|
|
|
* look in its default locations. |
2233
|
|
|
* |
2234
|
|
|
* @return array |
2235
|
|
|
* @see isInstalledStandard() |
2236
|
|
|
*/ |
2237
|
|
|
public static function getInstalledStandards( |
2238
|
|
|
$includeGeneric=false, |
2239
|
|
|
$standardsDir='' |
2240
|
|
|
) { |
2241
|
|
|
$installedStandards = array(); |
2242
|
|
|
|
2243
|
|
|
if ($standardsDir === '') { |
2244
|
|
|
$installedPaths = self::getInstalledStandardPaths(); |
2245
|
|
|
} else { |
2246
|
|
|
$installedPaths = array($standardsDir); |
2247
|
|
|
} |
2248
|
|
|
|
2249
|
|
|
foreach ($installedPaths as $standardsDir) { |
2250
|
|
|
$di = new DirectoryIterator($standardsDir); |
2251
|
|
|
foreach ($di as $file) { |
2252
|
|
|
if ($file->isDir() === true && $file->isDot() === false) { |
2253
|
|
|
$filename = $file->getFilename(); |
2254
|
|
|
|
2255
|
|
|
// Ignore the special "Generic" standard. |
2256
|
|
|
if ($includeGeneric === false && $filename === 'Generic') { |
2257
|
|
|
continue; |
2258
|
|
|
} |
2259
|
|
|
|
2260
|
|
|
// Valid coding standard dirs include a ruleset. |
2261
|
|
|
$csFile = $file->getPathname().'/ruleset.xml'; |
2262
|
|
|
if (is_file($csFile) === true) { |
2263
|
|
|
$installedStandards[] = $filename; |
2264
|
|
|
} |
2265
|
|
|
} |
2266
|
|
|
} |
2267
|
|
|
}//end foreach |
2268
|
|
|
|
2269
|
|
|
return $installedStandards; |
2270
|
|
|
|
2271
|
|
|
}//end getInstalledStandards() |
2272
|
|
|
|
2273
|
|
|
|
2274
|
|
|
/** |
2275
|
|
|
* Determine if a standard is installed. |
2276
|
|
|
* |
2277
|
|
|
* Coding standards are directories located in the |
2278
|
|
|
* CodeSniffer/Standards directory. Valid coding standards |
2279
|
|
|
* include a ruleset.xml file. |
2280
|
|
|
* |
2281
|
|
|
* @param string $standard The name of the coding standard. |
2282
|
|
|
* |
2283
|
|
|
* @return boolean |
2284
|
|
|
* @see getInstalledStandards() |
2285
|
|
|
*/ |
2286
|
|
|
public static function isInstalledStandard($standard) |
2287
|
|
|
{ |
2288
|
|
|
$path = self::getInstalledStandardPath($standard); |
2289
|
|
|
if ($path !== null && strpos($path, 'ruleset.xml') !== false) { |
2290
|
|
|
return true; |
2291
|
|
|
} else { |
2292
|
|
|
// This could be a custom standard, installed outside our |
2293
|
|
|
// standards directory. |
2294
|
|
|
$standard = self::realPath($standard); |
2295
|
|
|
|
2296
|
|
|
// Might be an actual ruleset file itself. |
2297
|
|
|
// If it has an XML extension, let's at least try it. |
2298
|
|
|
if (is_file($standard) === true |
2299
|
|
|
&& (substr(strtolower($standard), -4) === '.xml' |
2300
|
|
|
|| substr(strtolower($standard), -9) === '.xml.dist') |
2301
|
|
|
) { |
2302
|
|
|
return true; |
2303
|
|
|
} |
2304
|
|
|
|
2305
|
|
|
// If it is a directory with a ruleset.xml file in it, |
2306
|
|
|
// it is a standard. |
2307
|
|
|
$ruleset = rtrim($standard, ' /\\').DIRECTORY_SEPARATOR.'ruleset.xml'; |
2308
|
|
|
if (is_file($ruleset) === true) { |
2309
|
|
|
return true; |
2310
|
|
|
} |
2311
|
|
|
}//end if |
2312
|
|
|
|
2313
|
|
|
return false; |
2314
|
|
|
|
2315
|
|
|
}//end isInstalledStandard() |
2316
|
|
|
|
2317
|
|
|
|
2318
|
|
|
/** |
2319
|
|
|
* Return the path of an installed coding standard. |
2320
|
|
|
* |
2321
|
|
|
* Coding standards are directories located in the |
2322
|
|
|
* CodeSniffer/Standards directory. Valid coding standards |
2323
|
|
|
* include a ruleset.xml file. |
2324
|
|
|
* |
2325
|
|
|
* @param string $standard The name of the coding standard. |
2326
|
|
|
* |
2327
|
|
|
* @return string|null |
2328
|
|
|
*/ |
2329
|
|
|
public static function getInstalledStandardPath($standard) |
2330
|
|
|
{ |
2331
|
|
|
$installedPaths = self::getInstalledStandardPaths(); |
2332
|
|
|
foreach ($installedPaths as $installedPath) { |
2333
|
|
|
$standardPath = $installedPath.DIRECTORY_SEPARATOR.$standard; |
2334
|
|
|
$path = self::realpath($standardPath.DIRECTORY_SEPARATOR.'ruleset.xml'); |
2335
|
|
|
if (is_file($path) === true) { |
2336
|
|
|
return $path; |
2337
|
|
|
} else if (self::isPharFile($standardPath) === true) { |
2338
|
|
|
$path = self::realpath($standardPath); |
2339
|
|
|
if ($path !== false) { |
2340
|
|
|
return $path; |
2341
|
|
|
} |
2342
|
|
|
} |
2343
|
|
|
} |
2344
|
|
|
|
2345
|
|
|
return null; |
2346
|
|
|
|
2347
|
|
|
}//end getInstalledStandardPath() |
2348
|
|
|
|
2349
|
|
|
|
2350
|
|
|
/** |
2351
|
|
|
* Get a single config value. |
2352
|
|
|
* |
2353
|
|
|
* Config data is stored in the data dir, in a file called |
2354
|
|
|
* CodeSniffer.conf. It is a simple PHP array. |
2355
|
|
|
* |
2356
|
|
|
* @param string $key The name of the config value. |
2357
|
|
|
* |
2358
|
|
|
* @return string|null |
2359
|
|
|
* @see setConfigData() |
2360
|
|
|
* @see getAllConfigData() |
2361
|
|
|
*/ |
2362
|
|
|
public static function getConfigData($key) |
2363
|
|
|
{ |
2364
|
|
|
$phpCodeSnifferConfig = self::getAllConfigData(); |
2365
|
|
|
|
2366
|
|
|
if ($phpCodeSnifferConfig === null) { |
2367
|
|
|
return null; |
2368
|
|
|
} |
2369
|
|
|
|
2370
|
|
|
if (isset($phpCodeSnifferConfig[$key]) === false) { |
2371
|
|
|
return null; |
2372
|
|
|
} |
2373
|
|
|
|
2374
|
|
|
return $phpCodeSnifferConfig[$key]; |
2375
|
|
|
|
2376
|
|
|
}//end getConfigData() |
2377
|
|
|
|
2378
|
|
|
|
2379
|
|
|
/** |
2380
|
|
|
* Set a single config value. |
2381
|
|
|
* |
2382
|
|
|
* Config data is stored in the data dir, in a file called |
2383
|
|
|
* CodeSniffer.conf. It is a simple PHP array. |
2384
|
|
|
* |
2385
|
|
|
* @param string $key The name of the config value. |
2386
|
|
|
* @param string|null $value The value to set. If null, the config |
2387
|
|
|
* entry is deleted, reverting it to the |
2388
|
|
|
* default value. |
2389
|
|
|
* @param boolean $temp Set this config data temporarily for this |
2390
|
|
|
* script run. This will not write the config |
2391
|
|
|
* data to the config file. |
2392
|
|
|
* |
2393
|
|
|
* @return boolean |
2394
|
|
|
* @see getConfigData() |
2395
|
|
|
* @throws PHP_CodeSniffer_Exception If the config file can not be written. |
2396
|
|
|
*/ |
2397
|
|
|
public static function setConfigData($key, $value, $temp=false) |
|
|
|
|
2398
|
|
|
{ |
2399
|
|
|
if ($temp === false) { |
2400
|
|
|
$path = ''; |
2401
|
|
|
if (is_callable('Phar::running') === true) { |
2402
|
|
|
$path = Phar::running(false); |
2403
|
|
|
} |
2404
|
|
|
|
2405
|
|
|
if ($path !== '') { |
2406
|
|
|
$configFile = dirname($path).'/CodeSniffer.conf'; |
2407
|
|
|
} else { |
2408
|
|
|
$configFile = dirname(__FILE__).'/CodeSniffer.conf'; |
2409
|
|
|
if (is_file($configFile) === false |
2410
|
|
|
&& strpos('@data_dir@', '@data_dir') === false |
2411
|
|
|
) { |
2412
|
|
|
// If data_dir was replaced, this is a PEAR install and we can |
2413
|
|
|
// use the PEAR data dir to store the conf file. |
2414
|
|
|
$configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf'; |
2415
|
|
|
} |
2416
|
|
|
} |
2417
|
|
|
|
2418
|
|
|
if (is_file($configFile) === true |
2419
|
|
|
&& is_writable($configFile) === false |
2420
|
|
|
) { |
2421
|
|
|
$error = 'Config file '.$configFile.' is not writable'; |
2422
|
|
|
throw new PHP_CodeSniffer_Exception($error); |
2423
|
|
|
} |
2424
|
|
|
}//end if |
2425
|
|
|
|
2426
|
|
|
$phpCodeSnifferConfig = self::getAllConfigData(); |
2427
|
|
|
|
2428
|
|
|
if ($value === null) { |
2429
|
|
|
if (isset($phpCodeSnifferConfig[$key]) === true) { |
2430
|
|
|
unset($phpCodeSnifferConfig[$key]); |
2431
|
|
|
} |
2432
|
|
|
} else { |
2433
|
|
|
$phpCodeSnifferConfig[$key] = $value; |
2434
|
|
|
} |
2435
|
|
|
|
2436
|
|
|
if ($temp === false) { |
2437
|
|
|
$output = '<'.'?php'."\n".' $phpCodeSnifferConfig = '; |
2438
|
|
|
$output .= var_export($phpCodeSnifferConfig, true); |
2439
|
|
|
$output .= "\n?".'>'; |
2440
|
|
|
|
2441
|
|
|
if (file_put_contents($configFile, $output) === false) { |
|
|
|
|
2442
|
|
|
return false; |
2443
|
|
|
} |
2444
|
|
|
} |
2445
|
|
|
|
2446
|
|
|
$GLOBALS['PHP_CODESNIFFER_CONFIG_DATA'] = $phpCodeSnifferConfig; |
2447
|
|
|
|
2448
|
|
|
return true; |
2449
|
|
|
|
2450
|
|
|
}//end setConfigData() |
2451
|
|
|
|
2452
|
|
|
|
2453
|
|
|
/** |
2454
|
|
|
* Get all config data in an array. |
2455
|
|
|
* |
2456
|
|
|
* @return array<string, string> |
|
|
|
|
2457
|
|
|
* @see getConfigData() |
2458
|
|
|
*/ |
2459
|
|
|
public static function getAllConfigData() |
|
|
|
|
2460
|
|
|
{ |
2461
|
|
|
if (isset($GLOBALS['PHP_CODESNIFFER_CONFIG_DATA']) === true) { |
2462
|
|
|
return $GLOBALS['PHP_CODESNIFFER_CONFIG_DATA']; |
2463
|
|
|
} |
2464
|
|
|
|
2465
|
|
|
$path = ''; |
2466
|
|
|
if (is_callable('Phar::running') === true) { |
2467
|
|
|
$path = Phar::running(false); |
2468
|
|
|
} |
2469
|
|
|
|
2470
|
|
|
if ($path !== '') { |
2471
|
|
|
$configFile = dirname($path).'/CodeSniffer.conf'; |
2472
|
|
|
} else { |
2473
|
|
|
$configFile = dirname(__FILE__).'/CodeSniffer.conf'; |
2474
|
|
|
if (is_file($configFile) === false) { |
2475
|
|
|
$configFile = '@data_dir@/PHP_CodeSniffer/CodeSniffer.conf'; |
2476
|
|
|
} |
2477
|
|
|
} |
2478
|
|
|
|
2479
|
|
|
if (is_file($configFile) === false) { |
2480
|
|
|
$GLOBALS['PHP_CODESNIFFER_CONFIG_DATA'] = array(); |
2481
|
|
|
return array(); |
2482
|
|
|
} |
2483
|
|
|
|
2484
|
|
|
include $configFile; |
2485
|
|
|
$GLOBALS['PHP_CODESNIFFER_CONFIG_DATA'] = $phpCodeSnifferConfig; |
|
|
|
|
2486
|
|
|
return $GLOBALS['PHP_CODESNIFFER_CONFIG_DATA']; |
2487
|
|
|
|
2488
|
|
|
}//end getAllConfigData() |
2489
|
|
|
|
2490
|
|
|
|
2491
|
|
|
/** |
2492
|
|
|
* Return TRUE, if the path is a phar file. |
2493
|
|
|
* |
2494
|
|
|
* @param string $path The path to use. |
2495
|
|
|
* |
2496
|
|
|
* @return mixed |
2497
|
|
|
*/ |
2498
|
|
|
public static function isPharFile($path) |
2499
|
|
|
{ |
2500
|
|
|
if (strpos($path, 'phar://') === 0) { |
2501
|
|
|
return true; |
2502
|
|
|
} |
2503
|
|
|
|
2504
|
|
|
return false; |
2505
|
|
|
|
2506
|
|
|
}//end isPharFile() |
2507
|
|
|
|
2508
|
|
|
|
2509
|
|
|
/** |
2510
|
|
|
* CodeSniffer alternative for realpath. |
2511
|
|
|
* |
2512
|
|
|
* Allows for phar support. |
2513
|
|
|
* |
2514
|
|
|
* @param string $path The path to use. |
2515
|
|
|
* |
2516
|
|
|
* @return mixed |
2517
|
|
|
*/ |
2518
|
|
|
public static function realpath($path) |
2519
|
|
|
{ |
2520
|
|
|
// Support the path replacement of ~ with the user's home directory. |
2521
|
|
|
if (substr($path, 0, 2) === '~/') { |
2522
|
|
|
$homeDir = getenv('HOME'); |
2523
|
|
|
if ($homeDir !== false) { |
2524
|
|
|
$path = $homeDir.substr($path, 1); |
2525
|
|
|
} |
2526
|
|
|
} |
2527
|
|
|
|
2528
|
|
|
// No extra work needed if this is not a phar file. |
2529
|
|
|
if (self::isPharFile($path) === false) { |
2530
|
|
|
return realpath($path); |
2531
|
|
|
} |
2532
|
|
|
|
2533
|
|
|
// Before trying to break down the file path, |
2534
|
|
|
// check if it exists first because it will mostly not |
2535
|
|
|
// change after running the below code. |
2536
|
|
|
if (file_exists($path) === true) { |
2537
|
|
|
return $path; |
2538
|
|
|
} |
2539
|
|
|
|
2540
|
|
|
$phar = Phar::running(false); |
2541
|
|
|
$extra = str_replace('phar://'.$phar, '', $path); |
2542
|
|
|
$path = realpath($phar); |
2543
|
|
|
if ($path === false) { |
2544
|
|
|
return false; |
2545
|
|
|
} |
2546
|
|
|
|
2547
|
|
|
$path = 'phar://'.$path.$extra; |
2548
|
|
|
if (file_exists($path) === true) { |
2549
|
|
|
return $path; |
2550
|
|
|
} |
2551
|
|
|
|
2552
|
|
|
return false; |
2553
|
|
|
|
2554
|
|
|
}//end realpath() |
2555
|
|
|
|
2556
|
|
|
|
2557
|
|
|
/** |
2558
|
|
|
* CodeSniffer alternative for chdir(). |
2559
|
|
|
* |
2560
|
|
|
* Allows for phar support. |
2561
|
|
|
* |
2562
|
|
|
* @param string $path The path to use. |
2563
|
|
|
* |
2564
|
|
|
* @return void |
2565
|
|
|
*/ |
2566
|
|
|
public static function chdir($path) |
2567
|
|
|
{ |
2568
|
|
|
if (self::isPharFile($path) === true) { |
2569
|
|
|
$phar = Phar::running(false); |
2570
|
|
|
chdir(dirname($phar)); |
2571
|
|
|
} else { |
2572
|
|
|
chdir($path); |
2573
|
|
|
} |
2574
|
|
|
|
2575
|
|
|
}//end chdir() |
2576
|
|
|
|
2577
|
|
|
|
2578
|
|
|
}//end class |
2579
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.