Chris@18
|
1 <?php
|
Chris@18
|
2 /**
|
Chris@18
|
3 * Validate the PHP_CodeSniffer PEAR package.xml file.
|
Chris@18
|
4 *
|
Chris@18
|
5 * PHP version 5
|
Chris@18
|
6 *
|
Chris@18
|
7 * @category PHP
|
Chris@18
|
8 * @package PHP_CodeSniffer
|
Chris@18
|
9 * @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
|
Chris@18
|
10 * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
|
Chris@18
|
11 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
|
Chris@18
|
12 */
|
Chris@18
|
13
|
Chris@18
|
14 /**
|
Chris@18
|
15 * Validate the PHP_CodeSniffer PEAR package.xml file.
|
Chris@18
|
16 */
|
Chris@18
|
17 class ValidatePEARPackageXML
|
Chris@18
|
18 {
|
Chris@18
|
19
|
Chris@18
|
20 /**
|
Chris@18
|
21 * The root directory of the project.
|
Chris@18
|
22 *
|
Chris@18
|
23 * @var string
|
Chris@18
|
24 */
|
Chris@18
|
25 protected $projectRoot = '';
|
Chris@18
|
26
|
Chris@18
|
27 /**
|
Chris@18
|
28 * The contents of the package.xml file.
|
Chris@18
|
29 *
|
Chris@18
|
30 * @var \SimpleXMLElement
|
Chris@18
|
31 */
|
Chris@18
|
32 protected $packageXML;
|
Chris@18
|
33
|
Chris@18
|
34 /**
|
Chris@18
|
35 * List of all files in the repo.
|
Chris@18
|
36 *
|
Chris@18
|
37 * @var array
|
Chris@18
|
38 */
|
Chris@18
|
39 protected $allFiles = [];
|
Chris@18
|
40
|
Chris@18
|
41 /**
|
Chris@18
|
42 * Valid file roles.
|
Chris@18
|
43 *
|
Chris@18
|
44 * @var array
|
Chris@18
|
45 *
|
Chris@18
|
46 * @link https://pear.php.net/manual/en/developers.packagedef.intro.php#developers.packagedef.roles
|
Chris@18
|
47 */
|
Chris@18
|
48 private $validRoles = [
|
Chris@18
|
49 'data' => true,
|
Chris@18
|
50 'doc' => true,
|
Chris@18
|
51 'ext' => true,
|
Chris@18
|
52 'extsrc' => true,
|
Chris@18
|
53 'php' => true,
|
Chris@18
|
54 'script' => true,
|
Chris@18
|
55 'src' => true,
|
Chris@18
|
56 'test' => true,
|
Chris@18
|
57 ];
|
Chris@18
|
58
|
Chris@18
|
59 /**
|
Chris@18
|
60 * Files encountered in the package.xml <contents> tag.
|
Chris@18
|
61 *
|
Chris@18
|
62 * @var array
|
Chris@18
|
63 */
|
Chris@18
|
64 private $listedContents = [];
|
Chris@18
|
65
|
Chris@18
|
66
|
Chris@18
|
67 /**
|
Chris@18
|
68 * Constructor.
|
Chris@18
|
69 */
|
Chris@18
|
70 public function __construct()
|
Chris@18
|
71 {
|
Chris@18
|
72 $this->projectRoot = dirname(dirname(__DIR__)).'/';
|
Chris@18
|
73 $this->packageXML = simplexml_load_file($this->projectRoot.'package.xml');
|
Chris@18
|
74
|
Chris@18
|
75 $allFiles = (new FileList($this->projectRoot, $this->projectRoot))->getList();
|
Chris@18
|
76 $this->allFiles = array_flip($allFiles);
|
Chris@18
|
77
|
Chris@18
|
78 }//end __construct()
|
Chris@18
|
79
|
Chris@18
|
80
|
Chris@18
|
81 /**
|
Chris@18
|
82 * Validate the file listings in the package.xml file.
|
Chris@18
|
83 *
|
Chris@18
|
84 * @return void
|
Chris@18
|
85 */
|
Chris@18
|
86 public function validate()
|
Chris@18
|
87 {
|
Chris@18
|
88 $exitCode = 0;
|
Chris@18
|
89 if ($this->checkContents() !== true) {
|
Chris@18
|
90 $exitCode = 1;
|
Chris@18
|
91 }
|
Chris@18
|
92
|
Chris@18
|
93 if ($this->checkPHPRelease() !== true) {
|
Chris@18
|
94 $exitCode = 1;
|
Chris@18
|
95 }
|
Chris@18
|
96
|
Chris@18
|
97 exit($exitCode);
|
Chris@18
|
98
|
Chris@18
|
99 }//end validate()
|
Chris@18
|
100
|
Chris@18
|
101
|
Chris@18
|
102 /**
|
Chris@18
|
103 * Validate the file listings in the <contents> tag.
|
Chris@18
|
104 *
|
Chris@18
|
105 * @return bool
|
Chris@18
|
106 */
|
Chris@18
|
107 protected function checkContents()
|
Chris@18
|
108 {
|
Chris@18
|
109 echo PHP_EOL.'Checking Contents tag'.PHP_EOL;
|
Chris@18
|
110 echo '====================='.PHP_EOL;
|
Chris@18
|
111
|
Chris@18
|
112 $valid = true;
|
Chris@18
|
113
|
Chris@18
|
114 /*
|
Chris@18
|
115 * - Check that every file that is mentioned in the `<content>` tag exists in the repo.
|
Chris@18
|
116 * - Check that the "role" value is valid.
|
Chris@18
|
117 * - Check that the "baseinstalldir" value is valid.
|
Chris@18
|
118 */
|
Chris@18
|
119
|
Chris@18
|
120 $valid = $this->walkDirTag($this->packageXML->contents);
|
Chris@18
|
121 if ($valid === true) {
|
Chris@18
|
122 echo "Existing listings in the Contents tag are valid.".PHP_EOL;
|
Chris@18
|
123 }
|
Chris@18
|
124
|
Chris@18
|
125 /*
|
Chris@18
|
126 * Verify that all files in the `src` and the `tests` directories are listed in the `<contents>` tag.
|
Chris@18
|
127 */
|
Chris@18
|
128
|
Chris@18
|
129 $srcFiles = (new FileList(
|
Chris@18
|
130 $this->projectRoot.'src/',
|
Chris@18
|
131 $this->projectRoot,
|
Chris@18
|
132 '`\.(css|fixed|inc|js|php|xml)$`Di'
|
Chris@18
|
133 ))->getList();
|
Chris@18
|
134 $testsFiles = (new FileList(
|
Chris@18
|
135 $this->projectRoot.'tests/',
|
Chris@18
|
136 $this->projectRoot,
|
Chris@18
|
137 '`\.(css|inc|js|php|xml)$`Di'
|
Chris@18
|
138 ))->getList();
|
Chris@18
|
139 $files = array_merge($srcFiles, $testsFiles);
|
Chris@18
|
140
|
Chris@18
|
141 foreach ($files as $file) {
|
Chris@18
|
142 if (isset($this->listedContents[$file]) === true) {
|
Chris@18
|
143 continue;
|
Chris@18
|
144 }
|
Chris@18
|
145
|
Chris@18
|
146 echo "- File '{$file}' is missing from Contents tag.".PHP_EOL;
|
Chris@18
|
147 $valid = false;
|
Chris@18
|
148 }
|
Chris@18
|
149
|
Chris@18
|
150 if ($valid === true) {
|
Chris@18
|
151 echo "No missing files in the Contents tag.".PHP_EOL;
|
Chris@18
|
152 }
|
Chris@18
|
153
|
Chris@18
|
154 return $valid;
|
Chris@18
|
155
|
Chris@18
|
156 }//end checkContents()
|
Chris@18
|
157
|
Chris@18
|
158
|
Chris@18
|
159 /**
|
Chris@18
|
160 * Validate all child tags within a <dir> tag.
|
Chris@18
|
161 *
|
Chris@18
|
162 * @param \SimpleXMLElement $tag The current XML tag to examine.
|
Chris@18
|
163 * @param string $currentDirectory The complete relative path to the
|
Chris@18
|
164 * directory being examined.
|
Chris@18
|
165 *
|
Chris@18
|
166 * @return bool
|
Chris@18
|
167 */
|
Chris@18
|
168 protected function walkDirTag($tag, $currentDirectory='')
|
Chris@18
|
169 {
|
Chris@18
|
170 $valid = true;
|
Chris@18
|
171 $name = (string) $tag['name'];
|
Chris@18
|
172 if ($name !== '/' && empty($name) === false) {
|
Chris@18
|
173 $currentDirectory .= $name.'/';
|
Chris@18
|
174 }
|
Chris@18
|
175
|
Chris@18
|
176 $children = $tag->children();
|
Chris@18
|
177 foreach ($children as $key => $value) {
|
Chris@18
|
178 if ($key === 'dir') {
|
Chris@18
|
179 if ($this->walkDirTag($value, $currentDirectory) === false) {
|
Chris@18
|
180 $valid = false;
|
Chris@18
|
181 }
|
Chris@18
|
182 }
|
Chris@18
|
183
|
Chris@18
|
184 if ($key === 'file') {
|
Chris@18
|
185 if ($this->checkFileTag($value, $currentDirectory) === false) {
|
Chris@18
|
186 $valid = false;
|
Chris@18
|
187 }
|
Chris@18
|
188 }
|
Chris@18
|
189 }
|
Chris@18
|
190
|
Chris@18
|
191 return $valid;
|
Chris@18
|
192
|
Chris@18
|
193 }//end walkDirTag()
|
Chris@18
|
194
|
Chris@18
|
195
|
Chris@18
|
196 /**
|
Chris@18
|
197 * Validate the information within a <file> tag.
|
Chris@18
|
198 *
|
Chris@18
|
199 * @param \SimpleXMLElement $tag The current XML tag to examine.
|
Chris@18
|
200 * @param string $currentDirectory The complete relative path to the
|
Chris@18
|
201 * directory being examined.
|
Chris@18
|
202 *
|
Chris@18
|
203 * @return bool
|
Chris@18
|
204 */
|
Chris@18
|
205 protected function checkFileTag($tag, $currentDirectory='')
|
Chris@18
|
206 {
|
Chris@18
|
207 $valid = true;
|
Chris@18
|
208 $attributes = $tag->attributes();
|
Chris@18
|
209 $baseinstalldir = (string) $attributes['baseinstalldir'];
|
Chris@18
|
210 $name = $currentDirectory.(string) $attributes['name'];
|
Chris@18
|
211 $role = (string) $attributes['role'];
|
Chris@18
|
212
|
Chris@18
|
213 $this->listedContents[$name] = true;
|
Chris@18
|
214
|
Chris@18
|
215 if (empty($name) === true) {
|
Chris@18
|
216 echo "- Name attribute missing.".PHP_EOL;
|
Chris@18
|
217 $valid = false;
|
Chris@18
|
218 } else {
|
Chris@18
|
219 if (isset($this->allFiles[$name]) === false) {
|
Chris@18
|
220 echo "- File '{$name}' does not exist.".PHP_EOL;
|
Chris@18
|
221 $valid = false;
|
Chris@18
|
222 }
|
Chris@18
|
223
|
Chris@18
|
224 if (empty($role) === true) {
|
Chris@18
|
225 echo "- Role attribute missing for file '{$name}'.".PHP_EOL;
|
Chris@18
|
226 $valid = false;
|
Chris@18
|
227 } else {
|
Chris@18
|
228 if (isset($this->validRoles[$role]) === false) {
|
Chris@18
|
229 echo "- Role for file '{$name}' is invalid.".PHP_EOL;
|
Chris@18
|
230 $valid = false;
|
Chris@18
|
231 } else {
|
Chris@18
|
232 // Limited validation of the "role" tags.
|
Chris@18
|
233 if (strpos($name, 'Test.') !== false && $role !== 'test') {
|
Chris@18
|
234 echo "- Test files should have the role 'test'. Found: '$role' for file '{$name}'.".PHP_EOL;
|
Chris@18
|
235 $valid = false;
|
Chris@18
|
236 } else if ((strpos($name, 'Standard.xml') !== false || strpos($name, 'Sniff.php') !== false)
|
Chris@18
|
237 && $role !== 'php'
|
Chris@18
|
238 ) {
|
Chris@18
|
239 echo "- Sniff files, including sniff documentation files should have the role 'php'. Found: '$role' for file '{$name}'.".PHP_EOL;
|
Chris@18
|
240 $valid = false;
|
Chris@18
|
241 }
|
Chris@18
|
242 }
|
Chris@18
|
243
|
Chris@18
|
244 if (empty($baseinstalldir) === true) {
|
Chris@18
|
245 if ($role !== 'script' && strpos($name, 'tests/') !== 0) {
|
Chris@18
|
246 echo "- Baseinstalldir attribute missing for file '{$name}'.".PHP_EOL;
|
Chris@18
|
247 $valid = false;
|
Chris@18
|
248 }
|
Chris@18
|
249 } else {
|
Chris@18
|
250 if ($role === 'script' || strpos($name, 'tests/') === 0) {
|
Chris@18
|
251 echo "- Baseinstalldir for file '{$name}' should be empty.".PHP_EOL;
|
Chris@18
|
252 $valid = false;
|
Chris@18
|
253 }
|
Chris@18
|
254
|
Chris@18
|
255 if ($role !== 'script' && $baseinstalldir !== 'PHP/CodeSniffer') {
|
Chris@18
|
256 echo "- Baseinstalldir for file '{$name}' is invalid.".PHP_EOL;
|
Chris@18
|
257 $valid = false;
|
Chris@18
|
258 }
|
Chris@18
|
259 }
|
Chris@18
|
260 }//end if
|
Chris@18
|
261 }//end if
|
Chris@18
|
262
|
Chris@18
|
263 return $valid;
|
Chris@18
|
264
|
Chris@18
|
265 }//end checkFileTag()
|
Chris@18
|
266
|
Chris@18
|
267
|
Chris@18
|
268 /**
|
Chris@18
|
269 * Validate the file listings in the <phprelease> tags.
|
Chris@18
|
270 *
|
Chris@18
|
271 * @return bool True if the info in the "phprelease" tags is valid. False otherwise.
|
Chris@18
|
272 */
|
Chris@18
|
273 protected function checkPHPRelease()
|
Chris@18
|
274 {
|
Chris@18
|
275 echo PHP_EOL.'Checking PHPRelease tags'.PHP_EOL;
|
Chris@18
|
276 echo '========================'.PHP_EOL;
|
Chris@18
|
277
|
Chris@18
|
278 $valid = true;
|
Chris@18
|
279 $listedFiles = [];
|
Chris@18
|
280 $releaseTags = 1;
|
Chris@18
|
281
|
Chris@18
|
282 /*
|
Chris@18
|
283 * - Check that every file that is mentioned in the `<phprelease>` tags exists in the repo.
|
Chris@18
|
284 * - Check that the "as" value is valid.
|
Chris@18
|
285 */
|
Chris@18
|
286
|
Chris@18
|
287 foreach ($this->packageXML->phprelease as $release) {
|
Chris@18
|
288 foreach ($release->filelist->install as $install) {
|
Chris@18
|
289 $attributes = $install->attributes();
|
Chris@18
|
290 $name = (string) $attributes['name'];
|
Chris@18
|
291 $as = (string) $attributes['as'];
|
Chris@18
|
292
|
Chris@18
|
293 $listedFiles[$releaseTags][$name] = $as;
|
Chris@18
|
294
|
Chris@18
|
295 if (empty($as) === true || empty($name) === true) {
|
Chris@18
|
296 continue;
|
Chris@18
|
297 }
|
Chris@18
|
298
|
Chris@18
|
299 if (isset($this->allFiles[$name]) === false) {
|
Chris@18
|
300 echo "- File '{$name}' does not exist.".PHP_EOL;
|
Chris@18
|
301 $valid = false;
|
Chris@18
|
302 }
|
Chris@18
|
303
|
Chris@18
|
304 // Rest of the checks only apply to the test files.
|
Chris@18
|
305 if (strpos($name, 'tests/') !== 0) {
|
Chris@18
|
306 continue;
|
Chris@18
|
307 }
|
Chris@18
|
308
|
Chris@18
|
309 // Check validity of the tags for files in the tests root directory.
|
Chris@18
|
310 if (preg_match('`^tests/([^/]+\.php)$`', $name, $matches) === 1
|
Chris@18
|
311 && ($as === $name || $as === $matches[1])
|
Chris@18
|
312 ) {
|
Chris@18
|
313 continue;
|
Chris@18
|
314 }
|
Chris@18
|
315
|
Chris@18
|
316 // Check validity of the tags for files in the tests root subdirectories.
|
Chris@18
|
317 if (preg_match('`^tests/.+\.(php|inc|js|css|xml)$`', $name) === 1
|
Chris@18
|
318 && $as === str_replace('tests/', 'CodeSniffer/', $name)
|
Chris@18
|
319 ) {
|
Chris@18
|
320 continue;
|
Chris@18
|
321 }
|
Chris@18
|
322
|
Chris@18
|
323 echo "- Invalid 'as' attribute '{$as}' for test file '{$name}'.".PHP_EOL;
|
Chris@18
|
324 $valid = false;
|
Chris@18
|
325 }//end foreach
|
Chris@18
|
326
|
Chris@18
|
327 ++$releaseTags;
|
Chris@18
|
328 }//end foreach
|
Chris@18
|
329
|
Chris@18
|
330 if ($valid === true) {
|
Chris@18
|
331 echo "Existing PHPRelease tags are valid.".PHP_EOL;
|
Chris@18
|
332 }
|
Chris@18
|
333
|
Chris@18
|
334 /*
|
Chris@18
|
335 * Verify that all files in the `tests` directory are listed in both `<phprelease>` tags.
|
Chris@18
|
336 */
|
Chris@18
|
337
|
Chris@18
|
338 $testFiles = (new FileList($this->projectRoot.'tests/', $this->projectRoot, '`\.(inc|php|js|css|xml)$`Di'))->getList();
|
Chris@18
|
339
|
Chris@18
|
340 foreach ($testFiles as $file) {
|
Chris@18
|
341 foreach ($listedFiles as $key => $listed) {
|
Chris@18
|
342 if (isset($listed[$file]) === true) {
|
Chris@18
|
343 continue;
|
Chris@18
|
344 }
|
Chris@18
|
345
|
Chris@18
|
346 echo "- File '{$file}' is missing from PHPRelease tag [{$key}] .".PHP_EOL;
|
Chris@18
|
347 $valid = false;
|
Chris@18
|
348 }
|
Chris@18
|
349 }
|
Chris@18
|
350
|
Chris@18
|
351 if ($valid === true) {
|
Chris@18
|
352 echo "No missing PHPRelease tags.".PHP_EOL;
|
Chris@18
|
353 }
|
Chris@18
|
354
|
Chris@18
|
355 return $valid;
|
Chris@18
|
356
|
Chris@18
|
357 }//end checkPHPRelease()
|
Chris@18
|
358
|
Chris@18
|
359
|
Chris@18
|
360 }//end class
|