1 | <?php |
||||
2 | /** |
||||
3 | * This file is part of FSConsoleTools |
||||
4 | * Copyright (C) 2018 Francesc Pineda Segarra <[email protected]> |
||||
5 | * |
||||
6 | * This program is free software: you can redistribute it and/or modify |
||||
7 | * it under the terms of the GNU Lesser General Public License as |
||||
8 | * published by the Free Software Foundation, either version 3 of the |
||||
9 | * License, or (at your option) any later version. |
||||
10 | * |
||||
11 | * This program is distributed in the hope that it will be useful, |
||||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
14 | * GNU Lesser General Public License for more details. |
||||
15 | * |
||||
16 | * You should have received a copy of the GNU Lesser General Public License |
||||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
18 | */ |
||||
19 | |||||
20 | namespace FacturaScriptsUtils\Console\Command; |
||||
21 | |||||
22 | use DOMDocument; |
||||
23 | use FacturaScripts\Core\Base\FileManager; |
||||
0 ignored issues
–
show
|
|||||
24 | use FacturaScriptsUtils\Console\ConsoleAbstract; |
||||
25 | use SimpleXMLElement; |
||||
26 | |||||
27 | /** |
||||
28 | * Class OrderXmlTable |
||||
29 | * |
||||
30 | * @author Francesc Pineda Segarra <[email protected]> |
||||
31 | */ |
||||
32 | class OrderXmlTables extends ConsoleAbstract |
||||
33 | { |
||||
34 | /** |
||||
35 | * Constant values for return, to easy know how execution ends. |
||||
36 | */ |
||||
37 | const RETURN_SUCCESS = 0; |
||||
38 | const RETURN_SRC_FOLDER_NOT_SET = 1; |
||||
39 | const RETURN_DST_FOLDER_NOT_SET = 2; |
||||
40 | const RETURN_TAG_NAME_NOT_SET = 3; |
||||
41 | const RETURN_CANT_CREATE_FOLDER = 4; |
||||
42 | const RETURN_FAIL_SAVING_FILE = 5; |
||||
43 | const RETURN_NO_FILES = 6; |
||||
44 | const RETURN_SRC_FOLDER_NOT_EXISTS = 7; |
||||
45 | const RETURN_INCORRECT_FILE = 8; |
||||
46 | |||||
47 | /** |
||||
48 | * Folder source path. |
||||
49 | * |
||||
50 | * @var string |
||||
51 | */ |
||||
52 | private $srcFolder; |
||||
53 | |||||
54 | /** |
||||
55 | * Folder destiny path. |
||||
56 | * |
||||
57 | * @var string |
||||
58 | */ |
||||
59 | private $dstFolder; |
||||
60 | |||||
61 | /** |
||||
62 | * Tagname used for order. |
||||
63 | * |
||||
64 | * @var string |
||||
65 | */ |
||||
66 | private $tagName; |
||||
67 | |||||
68 | /** |
||||
69 | * Set default source folder. |
||||
70 | * |
||||
71 | * @param string $srcFolder |
||||
72 | * |
||||
73 | * @return $this |
||||
74 | */ |
||||
75 | public function setSrcFolder(string $srcFolder): self |
||||
76 | { |
||||
77 | $this->srcFolder = $srcFolder; |
||||
78 | return $this; |
||||
79 | } |
||||
80 | |||||
81 | /** |
||||
82 | * Set default destiny folder. |
||||
83 | * |
||||
84 | * @param string $dstFolder |
||||
85 | * |
||||
86 | * @return $this |
||||
87 | */ |
||||
88 | public function setDstFolder(string $dstFolder): self |
||||
89 | { |
||||
90 | $this->dstFolder = $dstFolder; |
||||
91 | return $this; |
||||
92 | } |
||||
93 | |||||
94 | /** |
||||
95 | * Set tag name. |
||||
96 | * |
||||
97 | * @param string $tagName |
||||
98 | * |
||||
99 | * @return $this |
||||
100 | */ |
||||
101 | public function setTagName(string $tagName): self |
||||
102 | { |
||||
103 | $this->tagName = $tagName; |
||||
104 | return $this; |
||||
105 | } |
||||
106 | |||||
107 | /** |
||||
108 | * Run the OrderXmlTable script. |
||||
109 | * |
||||
110 | * @param array $params |
||||
111 | * |
||||
112 | * @return int |
||||
113 | * @throws \Exception |
||||
114 | */ |
||||
115 | public function run(...$params): int |
||||
116 | { |
||||
117 | $this->autoReply = (bool) $params[2] ?? false; |
||||
118 | $this->autoHide = (bool) $params[3] ?? false; |
||||
119 | |||||
120 | $status = $this->checkOptions($params); |
||||
121 | if ($status !== 0) { |
||||
122 | return $status; |
||||
123 | } |
||||
124 | |||||
125 | $this->setSrcFolder(\FS_FOLDER . ($params[0] ?? 'Core/Table/')); |
||||
126 | $this->setDstFolder(\FS_FOLDER . ($params[1] ?? 'Core/Table/')); |
||||
127 | $this->setTagName($params[2] ?? 'name'); |
||||
128 | |||||
129 | $this->showMessage('Ordering XML table content' . \PHP_EOL . \PHP_EOL); |
||||
130 | $this->showMessage(' Options setted:' . \PHP_EOL); |
||||
131 | $this->showMessage(' Source path: ' . $this->srcFolder . \PHP_EOL); |
||||
132 | $this->showMessage(' Destiny path: ' . $this->dstFolder . \PHP_EOL); |
||||
133 | $this->showMessage(' Tag name: ' . $this->tagName . \PHP_EOL); |
||||
134 | |||||
135 | if (!$this->areYouSure($this->autoReply)) { |
||||
136 | $this->showMessage(' Options [SRC] [DST] [TAG]' . \PHP_EOL); |
||||
137 | return self::RETURN_SUCCESS; |
||||
138 | } |
||||
139 | |||||
140 | $status = $this->check(); |
||||
141 | if ($status !== 0) { |
||||
142 | return $status; |
||||
143 | } |
||||
144 | |||||
145 | $files = FileManager::scanFolder($this->srcFolder); |
||||
146 | |||||
147 | if (\count($files) === 0) { |
||||
148 | trigger_error('ERROR: No files on folder' . \PHP_EOL . \PHP_EOL); |
||||
149 | return self::RETURN_NO_FILES; |
||||
150 | } |
||||
151 | |||||
152 | return $this->orderXml($files); |
||||
153 | } |
||||
154 | |||||
155 | /** |
||||
156 | * Return description about this class. |
||||
157 | * |
||||
158 | * @return string |
||||
159 | */ |
||||
160 | public function getDescription(): string |
||||
161 | { |
||||
162 | return 'Order XML content files by tag name.'; |
||||
163 | } |
||||
164 | |||||
165 | /** |
||||
166 | * Print help information to the user. |
||||
167 | * |
||||
168 | * @return string |
||||
169 | */ |
||||
170 | public function getHelpMsg(): string |
||||
171 | { |
||||
172 | $array = \explode('\\', __CLASS__); |
||||
173 | $class = array_pop($array); |
||||
174 | return 'Use as: php vendor/bin/console ' . $class . ' [OPTIONS]' . \PHP_EOL |
||||
175 | . 'Available options:' . \PHP_EOL |
||||
176 | . ' -h, --help Show this help.' . \PHP_EOL |
||||
177 | . \PHP_EOL |
||||
178 | . ' OPTION1 Source path' . \PHP_EOL |
||||
179 | . ' OPTION2 Destiny path' . \PHP_EOL |
||||
180 | . ' OPTION3 Tag name' . \PHP_EOL |
||||
181 | . \PHP_EOL; |
||||
182 | } |
||||
183 | |||||
184 | /** |
||||
185 | * Returns an associative array of available methods for the user. |
||||
186 | * Add more options if you want to add support for custom methods. |
||||
187 | * [ |
||||
188 | * '-h' => 'getHelpMsg', |
||||
189 | * '--help' => 'getHelpMsg', |
||||
190 | * ] |
||||
191 | * |
||||
192 | * @return array |
||||
193 | */ |
||||
194 | public function getUserMethods(): array |
||||
195 | { |
||||
196 | return parent::getUserMethods(); |
||||
197 | } |
||||
198 | |||||
199 | /** |
||||
200 | * Order Xml files |
||||
201 | * |
||||
202 | * @param array $files |
||||
203 | * |
||||
204 | * @return int |
||||
205 | * @throws \Exception |
||||
206 | */ |
||||
207 | private function orderXml(array $files): int |
||||
208 | { |
||||
209 | try { |
||||
210 | foreach ($files as $fileName) { |
||||
211 | $xml = simplexml_load_string(file_get_contents($this->srcFolder . $fileName)); |
||||
212 | // Get all children of table into an array |
||||
213 | $table = (array) $xml->children(); |
||||
214 | $this->checkStructure($fileName, $table); |
||||
215 | $dom = new DOMDocument(); |
||||
216 | $dom->loadXML($this->generateXML($fileName, $table)); |
||||
217 | $filePath = $this->dstFolder . '/' . $fileName; |
||||
218 | if ($dom->save($filePath) === false) { |
||||
219 | trigger_error("ERROR: Can't save file " . $filePath . \PHP_EOL); |
||||
220 | return self::RETURN_FAIL_SAVING_FILE; |
||||
221 | } |
||||
222 | } |
||||
223 | } catch (\Exception $e) { |
||||
224 | trigger_error($e->getMessage(), \PHP_EOL); |
||||
0 ignored issues
–
show
PHP_EOL of type string is incompatible with the type integer expected by parameter $error_type of trigger_error() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
225 | return self::RETURN_INCORRECT_FILE; |
||||
226 | } |
||||
227 | $this->showMessage('Finished! Look at "' . $this->dstFolder . '"' . \PHP_EOL); |
||||
228 | return self::RETURN_SUCCESS; |
||||
229 | } |
||||
230 | |||||
231 | /** |
||||
232 | * Check if basic structure is correct |
||||
233 | * |
||||
234 | * @param string $fileName |
||||
235 | * @param array $table |
||||
236 | * |
||||
237 | * @throws \Exception |
||||
238 | */ |
||||
239 | private function checkStructure(string $fileName, array $table) |
||||
240 | { |
||||
241 | if (!isset($table['column'])) { |
||||
242 | throw new \RuntimeException( |
||||
243 | 'File is incorrect ' . $this->srcFolder . $fileName . \PHP_EOL |
||||
244 | . 'File does not contain any column.' . \PHP_EOL |
||||
245 | . 'Execution stopped' |
||||
246 | ); |
||||
247 | } |
||||
248 | foreach ($table as $key => $row) { |
||||
249 | $item = isset($key) ? 'column' : null; |
||||
250 | $item = $item ?? (isset($key) ? 'constraint' : null); |
||||
251 | if ($item === null) { |
||||
252 | throw new \RuntimeException( |
||||
253 | 'File is incorrect ' . $this->srcFolder . $fileName . \PHP_EOL |
||||
254 | . 'Unexpected data ' . print_r($key, true) . ' => ' . print_r($row, true) . '.' |
||||
255 | . 'Execution stopped' |
||||
256 | ); |
||||
257 | } |
||||
258 | if (isset($row[$item]) && !isset($row[$item]['name'], $row[$item]['type'])) { |
||||
259 | throw new \RuntimeException( |
||||
260 | 'File is incorrect ' . $this->srcFolder . $fileName . \PHP_EOL |
||||
261 | . \ucfirst($item) . ' ' . print_r($row[$item], true) . ' does not contain name or type.' |
||||
262 | . 'Execution stopped' |
||||
263 | ); |
||||
264 | } |
||||
265 | } |
||||
266 | } |
||||
267 | |||||
268 | /** |
||||
269 | * Return the final content of the XML file. |
||||
270 | * |
||||
271 | * @param string $fileName |
||||
272 | * @param array $table |
||||
273 | * |
||||
274 | * @return string |
||||
275 | */ |
||||
276 | private function generateXML(string $fileName, array $table): string |
||||
277 | { |
||||
278 | $columns = $table['column']; |
||||
279 | // Call usort on the array |
||||
280 | if (!\is_object($columns)) { |
||||
281 | usort($columns, [$this, 'sortName']); |
||||
282 | } |
||||
283 | // Generate string XML result |
||||
284 | $strXML = $this->generateXMLHeader($fileName); |
||||
285 | $strXML .= '<table>' . PHP_EOL; |
||||
286 | $strXML .= $this->generateXMLContent($columns); |
||||
287 | if (isset($table['constraint'])) { |
||||
288 | $constraints = $table['constraint']; |
||||
289 | if (!\is_object($constraints)) { |
||||
290 | usort($constraints, [$this, 'sortName']); |
||||
291 | } |
||||
292 | $strXML .= $this->generateXMLContent($constraints, 'constraint'); |
||||
293 | } |
||||
294 | $strXML .= '</table>'; |
||||
295 | return $strXML; |
||||
296 | } |
||||
297 | |||||
298 | /** |
||||
299 | * Check if options are looking for help. |
||||
300 | * |
||||
301 | * @param array $params |
||||
302 | * |
||||
303 | * @return int |
||||
304 | */ |
||||
305 | private function checkOptions(array $params = []): int |
||||
306 | { |
||||
307 | if (isset($params[0])) { |
||||
308 | switch ($params[0]) { |
||||
309 | case '-h': |
||||
310 | case '--help': |
||||
311 | $this->showMessage($this->getHelpMsg()); |
||||
312 | return -1; |
||||
313 | } |
||||
314 | } |
||||
315 | return 0; |
||||
316 | } |
||||
317 | |||||
318 | /** |
||||
319 | * Launch basic checks. |
||||
320 | * |
||||
321 | * @return int |
||||
322 | */ |
||||
323 | private function check(): int |
||||
324 | { |
||||
325 | if ($this->srcFolder === null) { |
||||
326 | trigger_error('ERROR: Source folder not setted.' . \PHP_EOL . \PHP_EOL); |
||||
327 | return self::RETURN_SRC_FOLDER_NOT_SET; |
||||
328 | } |
||||
329 | if ($this->dstFolder === null) { |
||||
330 | trigger_error('ERROR: Destiny folder not setted.' . \PHP_EOL . \PHP_EOL); |
||||
331 | return self::RETURN_DST_FOLDER_NOT_SET; |
||||
332 | } |
||||
333 | if ($this->tagName === null) { |
||||
334 | trigger_error('ERROR: Tag name not setted.' . \PHP_EOL . \PHP_EOL); |
||||
335 | return self::RETURN_TAG_NAME_NOT_SET; |
||||
336 | } |
||||
337 | if (!is_dir($this->srcFolder)) { |
||||
338 | trigger_error('ERROR: Source folder ' . $this->srcFolder . ' not exists.' . \PHP_EOL . \PHP_EOL); |
||||
339 | return self::RETURN_SRC_FOLDER_NOT_EXISTS; |
||||
340 | } |
||||
341 | if (!is_file($this->dstFolder) && !@mkdir($this->dstFolder) && !is_dir($this->dstFolder)) { |
||||
342 | trigger_error("ERROR: Can't create folder " . $this->dstFolder . '.' . \PHP_EOL . \PHP_EOL); |
||||
343 | return self::RETURN_CANT_CREATE_FOLDER; |
||||
344 | } |
||||
345 | return self::RETURN_SUCCESS; |
||||
346 | } |
||||
347 | |||||
348 | /** |
||||
349 | * Custom function to re-order with usort. |
||||
350 | * |
||||
351 | * @param SimpleXMLElement $xmlA |
||||
352 | * @param SimpleXMLElement $xmlB |
||||
353 | * |
||||
354 | * @return int |
||||
355 | */ |
||||
356 | private function sortName(SimpleXMLElement $xmlA, SimpleXMLElement $xmlB): int |
||||
357 | { |
||||
358 | return strcasecmp($xmlA->{$this->tagName}, $xmlB->{$this->tagName}); |
||||
359 | } |
||||
360 | |||||
361 | /** |
||||
362 | * Generate the content of the XML. |
||||
363 | * |
||||
364 | * @param array|object $data |
||||
365 | * @param string $tagName |
||||
366 | * |
||||
367 | * @return string |
||||
368 | */ |
||||
369 | private function generateXMLContent($data, $tagName = 'column'): string |
||||
370 | { |
||||
371 | $str = ''; |
||||
372 | if (\is_array($data)) { |
||||
373 | foreach ($data as $field) { |
||||
374 | $str .= ' <' . $tagName . '>' . PHP_EOL; |
||||
375 | foreach ((array) $field as $key => $value) { |
||||
376 | $str .= ' <' . $key . '>' . $value . '</' . $key . '>' . PHP_EOL; |
||||
377 | } |
||||
378 | $str .= ' </' . $tagName . '>' . PHP_EOL; |
||||
379 | } |
||||
380 | } |
||||
381 | if (\is_object($data)) { |
||||
382 | $str .= ' <' . $tagName . '>' . PHP_EOL; |
||||
383 | foreach ((array) $data as $key => $value) { |
||||
384 | $str .= ' <' . $key . '>' . $value . '</' . $key . '>' . PHP_EOL; |
||||
385 | } |
||||
386 | $str .= ' </' . $tagName . '>' . PHP_EOL; |
||||
387 | } |
||||
388 | |||||
389 | return $str; |
||||
390 | } |
||||
391 | |||||
392 | /** |
||||
393 | * Generate the header of the XML. |
||||
394 | * |
||||
395 | * @param string $fileName |
||||
396 | * |
||||
397 | * @return string |
||||
398 | */ |
||||
399 | private function generateXMLHeader(string $fileName): string |
||||
400 | { |
||||
401 | $str = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL; |
||||
402 | $str .= '<!--' . PHP_EOL; |
||||
403 | $str .= ' Document : ' . $fileName . PHP_EOL; |
||||
404 | $str .= ' Author : Ordered with OrderXmlTables from FSConsoleTools' . PHP_EOL; |
||||
405 | $str .= ' Description: Structure for the ' . str_replace('.xml', '', $fileName) . ' table.' . PHP_EOL; |
||||
406 | $str .= '-->' . PHP_EOL; |
||||
407 | return $str; |
||||
408 | } |
||||
409 | } |
||||
410 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths