1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of PHP Mess Detector. |
4
|
|
|
* |
5
|
|
|
* Copyright (c) Manuel Pichler <[email protected]>. |
6
|
|
|
* All rights reserved. |
7
|
|
|
* |
8
|
|
|
* Licensed under BSD License |
9
|
|
|
* For full copyright and license information, please see the LICENSE file. |
10
|
|
|
* Redistributions of files must retain the above copyright notice. |
11
|
|
|
* |
12
|
|
|
* @author Manuel Pichler <[email protected]> |
13
|
|
|
* @copyright Manuel Pichler. All rights reserved. |
14
|
|
|
* @license https://opensource.org/licenses/bsd-license.php BSD License |
15
|
|
|
* @link http://phpmd.org/ |
16
|
|
|
*/ |
17
|
|
|
|
18
|
|
|
namespace PHPMD; |
19
|
|
|
|
20
|
|
|
use RuntimeException; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* This factory class is used to create the {@link \PHPMD\RuleSet} instance |
24
|
|
|
* that PHPMD will use to analyze the source code. |
25
|
|
|
*/ |
26
|
|
|
class RuleSetFactory |
27
|
|
|
{ |
28
|
|
|
/** |
29
|
|
|
* Is the strict mode active? |
30
|
|
|
* |
31
|
|
|
* @var boolean |
32
|
|
|
* @since 1.2.0 |
33
|
|
|
*/ |
34
|
|
|
private $strict = false; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* The data directory set within the class constructor. |
38
|
|
|
* |
39
|
|
|
* @var string |
40
|
|
|
*/ |
41
|
|
|
private $location; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* The minimum priority for rules to load. |
45
|
|
|
* |
46
|
|
|
* @var integer |
47
|
|
|
*/ |
48
|
|
|
private $minimumPriority = Rule::LOWEST_PRIORITY; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* The maximum priority for rules to load. |
52
|
|
|
* |
53
|
|
|
* @var integer |
54
|
|
|
*/ |
55
|
|
|
private $maximumPriority = Rule::HIGHEST_PRIORITY; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Constructs a new default rule-set factory instance. |
59
|
48 |
|
*/ |
60
|
|
|
public function __construct() |
61
|
|
|
{ |
62
|
48 |
|
$this->location = __DIR__ . '/../../resources'; |
63
|
48 |
|
} |
64
|
48 |
|
|
65
|
|
|
/** |
66
|
|
|
* Activates the strict mode for all rule sets. |
67
|
|
|
* |
68
|
|
|
* @return void |
69
|
|
|
* @since 1.2.0 |
70
|
|
|
*/ |
71
|
|
|
public function setStrict() |
72
|
|
|
{ |
73
|
|
|
$this->strict = true; |
74
|
|
|
} |
75
|
1 |
|
|
76
|
|
|
/** |
77
|
1 |
|
* Sets the minimum priority that a rule must have. |
78
|
1 |
|
* |
79
|
|
|
* @param integer $minimumPriority The minimum priority value. |
80
|
|
|
* @return void |
81
|
|
|
*/ |
82
|
|
|
public function setMinimumPriority($minimumPriority) |
83
|
|
|
{ |
84
|
|
|
$this->minimumPriority = $minimumPriority; |
85
|
|
|
} |
86
|
12 |
|
|
87
|
|
|
/** |
88
|
12 |
|
* Sets the maximum priority that a rule must have. |
89
|
12 |
|
* |
90
|
|
|
* @param integer $maximumPriority The maximum priority value. |
91
|
|
|
* @return void |
92
|
|
|
*/ |
93
|
|
|
public function setMaximumPriority($maximumPriority) |
94
|
|
|
{ |
95
|
|
|
$this->maximumPriority = $maximumPriority; |
96
|
|
|
} |
97
|
12 |
|
|
98
|
|
|
/** |
99
|
12 |
|
* Creates an array of rule-set instances for the given argument. |
100
|
12 |
|
* |
101
|
|
|
* @param string $ruleSetFileNames Comma-separated string of rule-set filenames or identifier. |
102
|
|
|
* @return \PHPMD\RuleSet[] |
103
|
|
|
*/ |
104
|
|
|
public function createRuleSets($ruleSetFileNames) |
105
|
|
|
{ |
106
|
|
|
$ruleSets = array(); |
107
|
|
|
|
108
|
40 |
|
$ruleSetFileName = strtok($ruleSetFileNames, ','); |
109
|
|
|
while ($ruleSetFileName !== false) { |
110
|
40 |
|
$ruleSets[] = $this->createSingleRuleSet($ruleSetFileName); |
111
|
|
|
|
112
|
40 |
|
$ruleSetFileName = strtok(','); |
113
|
40 |
|
} |
114
|
40 |
|
|
115
|
|
|
return $ruleSets; |
116
|
36 |
|
} |
117
|
|
|
|
118
|
36 |
|
/** |
119
|
|
|
* Creates a single rule-set instance for the given filename or identifier. |
120
|
|
|
* |
121
|
|
|
* @param string $ruleSetOrFileName The rule-set filename or identifier. |
122
|
|
|
* @return \PHPMD\RuleSet |
123
|
|
|
*/ |
124
|
|
|
public function createSingleRuleSet($ruleSetOrFileName) |
125
|
|
|
{ |
126
|
|
|
$fileName = $this->createRuleSetFileName($ruleSetOrFileName); |
127
|
46 |
|
|
128
|
|
|
return $this->parseRuleSetNode($fileName); |
129
|
46 |
|
} |
130
|
45 |
|
|
131
|
|
|
/** |
132
|
|
|
* Lists available rule-set identifiers. |
133
|
|
|
* |
134
|
|
|
* @return string[] |
135
|
|
|
*/ |
136
|
|
|
public function listAvailableRuleSets() |
137
|
|
|
{ |
138
|
4 |
|
return array_merge( |
139
|
|
|
self::listRuleSetsInDirectory($this->location . '/rulesets/'), |
140
|
4 |
|
self::listRuleSetsInDirectory(getcwd() . '/rulesets/') |
141
|
4 |
|
); |
142
|
4 |
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* This method creates the filename for a rule-set identifier or it returns |
146
|
|
|
* the input when it is already a filename. |
147
|
|
|
* |
148
|
|
|
* @param string $ruleSetOrFileName The rule-set filename or identifier. |
149
|
|
|
* @return string Path to rule set file name |
150
|
|
|
* @throws RuleSetNotFoundException Thrown if no readable file found |
151
|
|
|
*/ |
152
|
|
|
private function createRuleSetFileName($ruleSetOrFileName) |
153
|
|
|
{ |
154
|
48 |
|
foreach ($this->filePaths($ruleSetOrFileName) as $filePath) { |
155
|
|
|
if ($this->isReadableFile($filePath)) { |
156
|
48 |
|
return $filePath; |
157
|
48 |
|
} |
158
|
47 |
|
} |
159
|
|
|
|
160
|
|
|
throw new RuleSetNotFoundException($ruleSetOrFileName); |
161
|
|
|
} |
162
|
2 |
|
|
163
|
|
|
/** |
164
|
|
|
* Lists available rule-set identifiers in given directory. |
165
|
|
|
* |
166
|
|
|
* @param string $directory The directory to scan for rule-sets. |
167
|
|
|
* @return string[] |
168
|
|
|
*/ |
169
|
|
|
private static function listRuleSetsInDirectory($directory) |
170
|
|
|
{ |
171
|
4 |
|
$ruleSets = array(); |
172
|
|
|
if (is_dir($directory)) { |
173
|
4 |
|
foreach (scandir($directory) as $file) { |
174
|
4 |
|
$matches = array(); |
175
|
4 |
|
if (is_file($directory . $file) && preg_match('/^(.*)\.xml$/', $file, $matches)) { |
176
|
4 |
|
$ruleSets[] = $matches[1]; |
177
|
4 |
|
} |
178
|
4 |
|
} |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
return $ruleSets; |
182
|
4 |
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* This method parses the rule-set definition in the given file. |
186
|
|
|
* |
187
|
|
|
* @param string $fileName |
188
|
|
|
* @return \PHPMD\RuleSet |
189
|
|
|
* @throws RuntimeException When loading the XML file fails. |
190
|
|
|
*/ |
191
|
45 |
|
private function parseRuleSetNode($fileName) |
192
|
|
|
{ |
193
|
|
|
// Hide error messages |
194
|
45 |
|
$libxml = libxml_use_internal_errors(true); |
195
|
|
|
|
196
|
45 |
|
$xml = simplexml_load_string(file_get_contents($fileName)); |
197
|
45 |
View Code Duplication |
if ($xml === false) { |
|
|
|
|
198
|
|
|
// Reset error handling to previous setting |
199
|
1 |
|
libxml_use_internal_errors($libxml); |
200
|
|
|
|
201
|
1 |
|
throw new RuntimeException(trim(libxml_get_last_error()->message)); |
202
|
|
|
} |
203
|
|
|
|
204
|
44 |
|
$ruleSet = new RuleSet(); |
205
|
44 |
|
$ruleSet->setFileName($fileName); |
206
|
44 |
|
$ruleSet->setName((string)$xml['name']); |
207
|
|
|
|
208
|
44 |
|
if ($this->strict) { |
209
|
1 |
|
$ruleSet->setStrict(); |
210
|
|
|
} |
211
|
|
|
|
212
|
44 |
|
foreach ($xml->children() as $node) { |
213
|
|
|
if ($node->getName() === 'php-includepath') { |
214
|
44 |
|
$includePath = (string)$node; |
215
|
1 |
|
|
216
|
|
|
if (is_dir(dirname($fileName) . DIRECTORY_SEPARATOR . $includePath)) { |
217
|
1 |
|
$includePath = dirname($fileName) . DIRECTORY_SEPARATOR . $includePath; |
218
|
|
|
$includePath = realpath($includePath); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
$includePath = get_include_path() . PATH_SEPARATOR . $includePath; |
222
|
1 |
|
set_include_path($includePath); |
223
|
1 |
|
} |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
foreach ($xml->children() as $node) { |
227
|
44 |
|
if ($node->getName() === 'description') { |
228
|
44 |
|
$ruleSet->setDescription((string)$node); |
229
|
44 |
|
} elseif ($node->getName() === 'rule') { |
230
|
44 |
|
$this->parseRuleNode($ruleSet, $node); |
231
|
44 |
|
} |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
return $ruleSet; |
235
|
42 |
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* This method parses a single rule xml node. Bases on the structure of the |
239
|
|
|
* xml node this method delegates the parsing process to another method in |
240
|
|
|
* this class. |
241
|
|
|
* |
242
|
|
|
* @param \PHPMD\RuleSet $ruleSet |
243
|
|
|
* @param \SimpleXMLElement $node |
244
|
|
|
* @return void |
245
|
|
|
*/ |
246
|
|
|
private function parseRuleNode(RuleSet $ruleSet, \SimpleXMLElement $node) |
247
|
44 |
|
{ |
248
|
|
|
if (substr($node['ref'], -3, 3) === 'xml') { |
249
|
44 |
|
$this->parseRuleSetReferenceNode($ruleSet, $node); |
250
|
6 |
|
|
251
|
6 |
|
return; |
252
|
|
|
} |
253
|
44 |
|
if ('' === (string)$node['ref']) { |
254
|
44 |
|
$this->parseSingleRuleNode($ruleSet, $node); |
255
|
42 |
|
|
256
|
|
|
return; |
257
|
8 |
|
} |
258
|
8 |
|
$this->parseRuleReferenceNode($ruleSet, $node); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* This method parses a complete rule set that was includes a reference in |
263
|
|
|
* the currently parsed ruleset. |
264
|
|
|
* |
265
|
|
|
* @param \PHPMD\RuleSet $ruleSet |
266
|
|
|
* @param \SimpleXMLElement $ruleSetNode |
267
|
|
|
* @return void |
268
|
6 |
|
*/ |
269
|
|
|
private function parseRuleSetReferenceNode(RuleSet $ruleSet, \SimpleXMLElement $ruleSetNode) |
270
|
6 |
|
{ |
271
|
6 |
|
$rules = $this->parseRuleSetReference($ruleSetNode); |
272
|
6 |
|
foreach ($rules as $rule) { |
273
|
5 |
|
if ($this->isIncluded($rule, $ruleSetNode)) { |
274
|
|
|
$ruleSet->addRule($rule); |
275
|
|
|
} |
276
|
6 |
|
} |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* Parses a rule-set xml file referenced by the given rule-set xml element. |
281
|
|
|
* |
282
|
|
|
* @param \SimpleXMLElement $ruleSetNode |
283
|
|
|
* @return \PHPMD\RuleSet |
284
|
|
|
* @since 0.2.3 |
285
|
6 |
|
*/ |
286
|
|
|
private function parseRuleSetReference(\SimpleXMLElement $ruleSetNode) |
287
|
6 |
|
{ |
288
|
6 |
|
$ruleSetFactory = new RuleSetFactory(); |
289
|
6 |
|
$ruleSetFactory->setMinimumPriority($this->minimumPriority); |
290
|
|
|
$ruleSetFactory->setMaximumPriority($this->maximumPriority); |
291
|
6 |
|
|
292
|
|
|
return $ruleSetFactory->createSingleRuleSet((string)$ruleSetNode['ref']); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* Checks if the given rule is included/not excluded by the given rule-set |
297
|
|
|
* reference node. |
298
|
|
|
* |
299
|
|
|
* @param \PHPMD\Rule $rule |
300
|
|
|
* @param \SimpleXMLElement $ruleSetNode |
301
|
|
|
* @return boolean |
302
|
|
|
* @since 0.2.3 |
303
|
6 |
|
*/ |
304
|
|
|
private function isIncluded(Rule $rule, \SimpleXMLElement $ruleSetNode) |
305
|
6 |
|
{ |
306
|
2 |
|
foreach ($ruleSetNode->exclude as $exclude) { |
307
|
2 |
|
if ($rule->getName() === (string)$exclude['name']) { |
308
|
|
|
return false; |
309
|
|
|
} |
310
|
5 |
|
} |
311
|
|
|
|
312
|
|
|
return true; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
/** |
316
|
|
|
* This method will create a single rule instance and add it to the given |
317
|
|
|
* {@link \PHPMD\RuleSet} object. |
318
|
|
|
* |
319
|
|
|
* @param \PHPMD\RuleSet $ruleSet |
320
|
|
|
* @param \SimpleXMLElement $ruleNode |
321
|
|
|
* @return void |
322
|
|
|
* @throws RuleClassFileNotFoundException |
323
|
44 |
|
* @throws RuleClassNotFoundException |
324
|
|
|
*/ |
325
|
44 |
|
private function parseSingleRuleNode(RuleSet $ruleSet, \SimpleXMLElement $ruleNode) |
326
|
|
|
{ |
327
|
44 |
|
$fileName = ""; |
328
|
|
|
|
329
|
44 |
|
$ruleSetFolderPath = dirname($ruleSet->getFileName()); |
330
|
1 |
|
|
331
|
|
|
if (isset($ruleNode['file'])) { |
332
|
1 |
|
if (is_readable((string)$ruleNode['file'])) { |
333
|
1 |
|
$fileName = (string)$ruleNode['file']; |
334
|
|
|
} elseif (is_readable($ruleSetFolderPath . DIRECTORY_SEPARATOR . (string)$ruleNode['file'])) { |
335
|
|
|
$fileName = $ruleSetFolderPath . DIRECTORY_SEPARATOR . (string)$ruleNode['file']; |
336
|
|
|
} |
337
|
44 |
|
} |
338
|
|
|
|
339
|
44 |
|
$className = (string)$ruleNode['class']; |
340
|
44 |
|
|
341
|
|
|
if (!is_readable($fileName)) { |
342
|
|
|
$fileName = strtr($className, '\\', '/') . '.php'; |
343
|
44 |
|
} |
344
|
44 |
|
|
345
|
|
|
if (!is_readable($fileName)) { |
346
|
|
|
$fileName = str_replace(array('\\', '_'), '/', $className) . '.php'; |
347
|
44 |
|
} |
348
|
3 |
|
|
349
|
3 |
|
if (class_exists($className) === false) { |
350
|
1 |
|
$handle = @fopen($fileName, 'r', true); |
351
|
|
|
if ($handle === false) { |
352
|
2 |
|
throw new RuleClassFileNotFoundException($className); |
353
|
|
|
} |
354
|
2 |
|
fclose($handle); |
355
|
|
|
|
356
|
2 |
|
include_once $fileName; |
357
|
1 |
|
|
358
|
|
|
if (class_exists($className) === false) { |
359
|
|
|
throw new RuleClassNotFoundException($className); |
360
|
|
|
} |
361
|
|
|
} |
362
|
42 |
|
|
363
|
42 |
|
/* @var $rule \PHPMD\Rule */ |
364
|
42 |
|
$rule = new $className(); |
365
|
42 |
|
$rule->setName((string)$ruleNode['name']); |
366
|
|
|
$rule->setMessage((string)$ruleNode['message']); |
367
|
42 |
|
$rule->setExternalInfoUrl((string)$ruleNode['externalInfoUrl']); |
368
|
|
|
|
369
|
42 |
|
$rule->setRuleSetName($ruleSet->getName()); |
370
|
42 |
|
|
371
|
|
|
if (trim($ruleNode['since']) !== '') { |
372
|
|
|
$rule->setSince((string)$ruleNode['since']); |
373
|
42 |
|
} |
374
|
|
|
|
375
|
42 |
View Code Duplication |
foreach ($ruleNode->children() as $node) { |
|
|
|
|
376
|
42 |
|
if ($node->getName() === 'description') { |
377
|
42 |
|
$rule->setDescription((string)$node); |
378
|
41 |
|
} elseif ($node->getName() === 'example') { |
379
|
42 |
|
$rule->addExample((string)$node); |
380
|
42 |
|
} elseif ($node->getName() === 'priority') { |
381
|
42 |
|
$rule->setPriority((integer)$node); |
382
|
42 |
|
} elseif ($node->getName() === 'properties') { |
383
|
|
|
$this->parsePropertiesNode($rule, $node); |
384
|
|
|
} |
385
|
|
|
} |
386
|
42 |
|
|
387
|
41 |
|
if ($rule->getPriority() <= $this->minimumPriority && $rule->getPriority() >= $this->maximumPriority) { |
388
|
|
|
$ruleSet->addRule($rule); |
389
|
42 |
|
} |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* This method parses a single rule that was included from a different |
394
|
|
|
* rule-set. |
395
|
|
|
* |
396
|
|
|
* @param \PHPMD\RuleSet $ruleSet |
397
|
|
|
* @param \SimpleXMLElement $ruleNode |
398
|
|
|
* @return void |
399
|
8 |
|
*/ |
400
|
|
|
private function parseRuleReferenceNode(RuleSet $ruleSet, \SimpleXMLElement $ruleNode) |
401
|
8 |
|
{ |
402
|
|
|
$ref = (string)$ruleNode['ref']; |
403
|
8 |
|
|
404
|
8 |
|
$fileName = substr($ref, 0, strpos($ref, '.xml/') + 4); |
405
|
|
|
$fileName = $this->createRuleSetFileName($fileName); |
406
|
8 |
|
|
407
|
|
|
$ruleName = substr($ref, strpos($ref, '.xml/') + 5); |
408
|
8 |
|
|
409
|
|
|
$ruleSetFactory = new RuleSetFactory(); |
410
|
8 |
|
|
411
|
8 |
|
$ruleSetRef = $ruleSetFactory->createSingleRuleSet($fileName); |
412
|
|
|
$rule = $ruleSetRef->getRuleByName($ruleName); |
413
|
8 |
|
|
414
|
3 |
|
if (trim($ruleNode['name']) !== '') { |
415
|
|
|
$rule->setName((string)$ruleNode['name']); |
416
|
8 |
|
} |
417
|
3 |
|
if (trim($ruleNode['message']) !== '') { |
418
|
|
|
$rule->setMessage((string)$ruleNode['message']); |
419
|
8 |
|
} |
420
|
3 |
|
if (trim($ruleNode['externalInfoUrl']) !== '') { |
421
|
|
|
$rule->setExternalInfoUrl((string)$ruleNode['externalInfoUrl']); |
422
|
|
|
} |
423
|
8 |
|
|
424
|
|
View Code Duplication |
foreach ($ruleNode->children() as $node) { |
|
|
|
|
425
|
4 |
|
if ($node->getName() === 'description') { |
426
|
4 |
|
$rule->setDescription((string)$node); |
427
|
4 |
|
} elseif ($node->getName() === 'example') { |
428
|
4 |
|
$rule->addExample((string)$node); |
429
|
4 |
|
} elseif ($node->getName() === 'priority') { |
430
|
4 |
|
$rule->setPriority((integer)$node); |
431
|
4 |
|
} elseif ($node->getName() === 'properties') { |
432
|
4 |
|
$this->parsePropertiesNode($rule, $node); |
433
|
|
|
} |
434
|
|
|
} |
435
|
|
|
|
436
|
8 |
|
if ($rule->getPriority() <= $this->minimumPriority && $rule->getPriority() >= $this->maximumPriority) { |
437
|
8 |
|
$ruleSet->addRule($rule); |
438
|
|
|
} |
439
|
8 |
|
} |
440
|
|
|
|
441
|
|
|
/** |
442
|
|
|
* This method parses a xml properties structure and adds all found properties |
443
|
|
|
* to the given <b>$rule</b> object. |
444
|
|
|
* |
445
|
|
|
* <code> |
446
|
|
|
* ... |
447
|
|
|
* <properties> |
448
|
|
|
* <property name="foo" value="42" /> |
449
|
|
|
* <property name="bar" value="23" /> |
450
|
|
|
* ... |
451
|
|
|
* </properties> |
452
|
|
|
* ... |
453
|
|
|
* </code> |
454
|
|
|
* |
455
|
|
|
* @param \PHPMD\Rule $rule |
456
|
|
|
* @param \SimpleXMLElement $propertiesNode |
457
|
|
|
* @return void |
458
|
|
|
*/ |
459
|
42 |
|
private function parsePropertiesNode(Rule $rule, \SimpleXMLElement $propertiesNode) |
460
|
|
|
{ |
461
|
42 |
|
foreach ($propertiesNode->children() as $node) { |
462
|
|
|
if ($node->getName() === 'property') { |
463
|
12 |
|
$this->addProperty($rule, $node); |
464
|
12 |
|
} |
465
|
|
|
} |
466
|
|
|
} |
467
|
42 |
|
|
468
|
|
|
/** |
469
|
|
|
* Adds an additional property to the given <b>$rule</b> instance. |
470
|
|
|
* |
471
|
|
|
* @param \PHPMD\Rule $rule |
472
|
|
|
* @param \SimpleXMLElement $node |
473
|
|
|
* @return void |
474
|
|
|
*/ |
475
|
|
|
private function addProperty(Rule $rule, \SimpleXMLElement $node) |
476
|
12 |
|
{ |
477
|
|
|
$name = trim($node['name']); |
478
|
12 |
|
$value = trim($this->getPropertyValue($node)); |
479
|
12 |
|
if ($name !== '' && $value !== '') { |
480
|
12 |
|
$rule->addProperty($name, $value); |
481
|
12 |
|
} |
482
|
|
|
} |
483
|
12 |
|
|
484
|
|
|
/** |
485
|
|
|
* Returns the value of a property node. This value can be expressed in |
486
|
|
|
* two different notations. First version is an attribute named <b>value</b> |
487
|
|
|
* and the second valid notation is a child element named <b>value</b> that |
488
|
|
|
* contains the value as character data. |
489
|
|
|
* |
490
|
|
|
* @param \SimpleXMLElement $propertyNode |
491
|
|
|
* @return string |
492
|
|
|
* @since 0.2.5 |
493
|
|
|
*/ |
494
|
|
|
private function getPropertyValue(\SimpleXMLElement $propertyNode) |
495
|
12 |
|
{ |
496
|
|
|
if (isset($propertyNode->value)) { |
497
|
12 |
|
return (string)$propertyNode->value; |
498
|
1 |
|
} |
499
|
|
|
|
500
|
11 |
|
return (string)$propertyNode['value']; |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
/** |
504
|
|
|
* Returns an array of path exclude patterns in format described at |
505
|
|
|
* |
506
|
|
|
* http://pmd.sourceforge.net/pmd-5.0.4/howtomakearuleset.html#Excluding_files_from_a_ruleset |
507
|
|
|
* |
508
|
|
|
* @param string $fileName The filename of a rule-set definition. |
509
|
|
|
* @return array|null |
510
|
|
|
* @throws RuntimeException Thrown if file is not proper xml |
511
|
|
|
*/ |
512
|
|
|
public function getIgnorePattern($fileName) |
513
|
8 |
|
{ |
514
|
|
|
$excludes = array(); |
515
|
8 |
|
foreach (array_map('trim', explode(',', $fileName)) as $ruleSetFileName) { |
516
|
8 |
|
$ruleSetFileName = $this->createRuleSetFileName($ruleSetFileName); |
517
|
8 |
|
|
518
|
|
|
// Hide error messages |
519
|
|
|
$libxml = libxml_use_internal_errors(true); |
520
|
8 |
|
|
521
|
|
|
$xml = simplexml_load_string(file_get_contents($ruleSetFileName)); |
522
|
8 |
View Code Duplication |
if ($xml === false) { |
|
|
|
|
523
|
8 |
|
// Reset error handling to previous setting |
524
|
|
|
libxml_use_internal_errors($libxml); |
525
|
|
|
|
526
|
|
|
throw new RuntimeException(trim(libxml_get_last_error()->message)); |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
foreach ($xml->children() as $node) { |
530
|
8 |
|
if ($node->getName() === 'exclude-pattern') { |
531
|
|
|
$excludes[] = '' . $node; |
532
|
8 |
|
} |
533
|
2 |
|
} |
534
|
|
|
|
535
|
|
|
return $excludes; |
536
|
|
|
} |
537
|
8 |
|
|
538
|
|
|
return null; |
539
|
|
|
} |
540
|
|
|
|
541
|
|
|
/** |
542
|
|
|
* Checks if given file path exists, is file (or symlink to file) |
543
|
|
|
* and is readable by current user |
544
|
|
|
* |
545
|
|
|
* @param string $filePath File path to check against |
546
|
|
|
* @return bool True if file exists and is readable, false otherwise |
547
|
|
|
*/ |
548
|
|
|
private function isReadableFile($filePath) |
549
|
48 |
|
{ |
550
|
|
|
return is_readable($filePath) && is_file($filePath); |
551
|
48 |
|
} |
552
|
47 |
|
|
553
|
|
|
/** |
554
|
27 |
|
* Returns list of possible file paths to search against code rules |
555
|
|
|
* |
556
|
|
|
* @param string $fileName Rule set file name |
557
|
|
|
* @return array Array of possible file locations |
558
|
|
|
*/ |
559
|
|
|
private function filePaths($fileName) |
560
|
|
|
{ |
561
|
|
|
$filePathParts = array( |
562
|
|
|
array($fileName), |
563
|
48 |
|
array($this->location, $fileName), |
564
|
|
|
array($this->location, 'rulesets', $fileName . '.xml'), |
565
|
|
|
array(getcwd(), 'rulesets', $fileName . '.xml'), |
566
|
48 |
|
); |
567
|
48 |
|
|
568
|
48 |
|
foreach (explode(PATH_SEPARATOR, get_include_path()) as $includePath) { |
569
|
48 |
|
$filePathParts[] = array($includePath, $fileName); |
570
|
|
|
$filePathParts[] = array($includePath, $fileName . '.xml'); |
571
|
|
|
} |
572
|
48 |
|
|
573
|
48 |
|
return array_map('implode', array_fill(0, count($filePathParts), DIRECTORY_SEPARATOR), $filePathParts); |
574
|
48 |
|
} |
575
|
|
|
} |
576
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.