| 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
Loading history...
|
|||||
| 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