Passed
Push — master ( 8014e2...1065ba )
by Francesc
01:48
created

OrderXmlTables::getHelpMsg()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
nc 1
nop 0
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
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
Bug introduced by
The type FacturaScripts\Core\Base\FileManager was not found. Maybe you did not declare it correctly or list all dependencies?

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:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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 $folderSrcPath;
53
54
    /**
55
     * Folder destiny path.
56
     *
57
     * @var string
58
     */
59
    private $folderDstPath;
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 $folderSrcPath
72
     *
73
     * @return $this
74
     */
75
    public function setFolderSrcPath(string $folderSrcPath): self
76
    {
77
        $this->folderSrcPath = $folderSrcPath;
78
        return $this;
79
    }
80
81
    /**
82
     * Set default destiny folder.
83
     *
84
     * @param string $folderDstPath
85
     *
86
     * @return $this
87
     */
88
    public function setFolderDstPath(string $folderDstPath): self
89
    {
90
        $this->folderDstPath = $folderDstPath;
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
        $status = $this->checkOptions($params);
118
        if ($status !== 0) {
119
            return $status;
120
        }
121
122
        $this->setFolderSrcPath($params[0] ?? \FS_FOLDER . 'Core/Table/');
123
        $this->setFolderDstPath($params[1] ?? \FS_FOLDER . 'Core/Table/');
124
        $this->setTagName($params[2] ?? 'name');
125
126
        echo 'Options setted:' . \PHP_EOL;
127
        echo '   Source path: ' . $this->folderSrcPath . \PHP_EOL;
128
        echo '   Destiny path: ' . $this->folderDstPath . \PHP_EOL;
129
        echo '   Tag name: ' . $this->tagName . \PHP_EOL;
130
        if (!$this->areYouSure()) {
131
            echo '   Options [SRC] [DST] [TAG]' . \PHP_EOL;
132
            return self::RETURN_SUCCESS;
133
        }
134
135
        $status = $this->check();
136
        if ($status !== 0) {
137
            return $status;
138
        }
139
140
        $files = FileManager::scanFolder($this->folderSrcPath);
141
142
        if (\count($files) === 0) {
143
            echo 'ERROR: No files on folder' . \PHP_EOL;
144
            return self::RETURN_NO_FILES;
145
        }
146
147
        return $this->orderXml($files);
148
    }
149
150
    /**
151
     * Return description about this class.
152
     *
153
     * @return string
154
     */
155
    public function getDescription(): string
156
    {
157
        return 'Order XML content files by tag name.';
158
    }
159
160
    /**
161
     * Print help information to the user.
162
     *
163
     * @return string
164
     */
165
    public function getHelpMsg(): string
166
    {
167
        $array = \explode('\\', __CLASS__);
168
        $class = array_pop($array);
169
        return 'Use as: php vendor/bin/console ' . $class . ' [OPTIONS]' . \PHP_EOL
170
            . 'Available options:' . \PHP_EOL
171
            . '   -h, --help        Show this help.' . \PHP_EOL
172
            . \PHP_EOL
173
            . '   OPTION1           Source path' . \PHP_EOL
174
            . '   OPTION2           Destiny path' . \PHP_EOL
175
            . '   OPTION3           Tag name' . \PHP_EOL
176
            . \PHP_EOL;
177
    }
178
179
    /**
180
     * Returns an associative array of available methods for the user.
181
     * Add more options if you want to add support for custom methods.
182
     *      [
183
     *          '-h'        => 'getHelpMsg',
184
     *          '--help'    => 'getHelpMsg',
185
     *      ]
186
     *
187
     * @return array
188
     */
189
    public function getUserMethods(): array
190
    {
191
        return [
192
            '-h' => 'getHelpMsg',
193
            '--help' => 'getHelpMsg'
194
        ];
195
    }
196
197
    /**
198
     * Order Xml files
199
     *
200
     * @param array $files
201
     *
202
     * @return int
203
     * @throws \Exception
204
     */
205
    private function orderXml(array $files): int
206
    {
207
        try {
208
            foreach ($files as $fileName) {
209
                $xml = simplexml_load_string(file_get_contents($this->folderSrcPath . $fileName));
210
                // Get all children of table into an array
211
                $table = (array) $xml->children();
212
                $this->checkStructure($fileName, $table);
213
                $dom = new DOMDocument();
214
                $dom->loadXML($this->generateXML($fileName, $table));
215
                $filePath = $this->folderDstPath . '/' . $fileName;
216
                if ($dom->save($filePath) === false) {
217
                    echo "ERROR: Can't save file " . $filePath . \PHP_EOL;
218
                    return self::RETURN_FAIL_SAVING_FILE;
219
                }
220
            }
221
        } catch (\Exception $e) {
222
            echo $e->getMessage(), \PHP_EOL;
223
            return self::RETURN_INCORRECT_FILE;
224
        }
225
        echo 'Finished! Look at "' . $this->folderDstPath . '"' . \PHP_EOL;
226
        return self::RETURN_SUCCESS;
227
    }
228
229
    /**
230
     * Check if basic structure is correct
231
     *
232
     * @param string $fileName
233
     * @param array  $table
234
     *
235
     * @throws \Exception
236
     */
237
    private function checkStructure(string $fileName, array $table)
238
    {
239
        if (!isset($table['column'])) {
240
            throw new \RuntimeException(
241
                'File is incorrect ' . $this->folderSrcPath . $fileName . \PHP_EOL
242
                . 'File does not contain any column.' . \PHP_EOL
243
                . 'Execution stopped'
244
            );
245
        }
246
        foreach ($table as $key => $row) {
247
            $item = isset($key) ? 'column' : null;
248
            $item = $item ?? (isset($key) ? 'constraint' : null);
249
            if ($item === null) {
250
                throw new \RuntimeException(
251
                    'File is incorrect ' . $this->folderSrcPath . $fileName . \PHP_EOL
252
                    . 'Unexpected data ' . print_r($key, true) . ' => ' . print_r($row, true) . '.'
253
                    . 'Execution stopped'
254
                );
255
            }
256
            if (isset($row[$item]) && !isset($row[$item]['name'], $row[$item]['type'])) {
257
                throw new \RuntimeException(
258
                    'File is incorrect ' . $this->folderSrcPath . $fileName . \PHP_EOL
259
                    . \ucfirst($item) . ' ' . print_r($row[$item], true) . ' does not contain name or type.'
260
                    . 'Execution stopped'
261
                );
262
            }
263
        }
264
    }
265
266
    /**
267
     * Return the final content of the XML file.
268
     *
269
     * @param string $fileName
270
     * @param array  $table
271
     *
272
     * @return string
273
     */
274
    private function generateXML(string $fileName, array $table): string
275
    {
276
        $columns = $table['column'];
277
        // Call usort on the array
278
        if (!\is_object($columns)) {
279
            usort($columns, [$this, 'sortName']);
280
        }
281
        // Generate string XML result
282
        $strXML = $this->generateXMLHeader($fileName);
283
        $strXML .= '<table>' . PHP_EOL;
284
        $strXML .= $this->generateXMLContent($columns);
285
        if (isset($table['constraint'])) {
286
            $constraints = $table['constraint'];
287
            if (!\is_object($constraints)) {
288
                usort($constraints, [$this, 'sortName']);
289
            }
290
            $strXML .= $this->generateXMLContent($constraints, 'constraint');
291
        }
292
        $strXML .= '</table>';
293
        return $strXML;
294
    }
295
296
    /**
297
     * Check if options are looking for help.
298
     *
299
     * @param array $params
300
     *
301
     * @return int
302
     */
303
    private function checkOptions(array $params = []): int
304
    {
305
        if (isset($params[0])) {
306
            switch ($params[0]) {
307
                case '-h':
308
                case '--help':
309
                    echo $this->getHelpMsg();
310
                    return -1;
311
            }
312
        }
313
        return 0;
314
    }
315
316
    /**
317
     * Launch basic checks.
318
     *
319
     * @return int
320
     */
321
    private function check(): int
322
    {
323
        if ($this->folderSrcPath === null) {
324
            echo 'ERROR: Source folder not setted.' . \PHP_EOL;
325
            return self::RETURN_SRC_FOLDER_NOT_SET;
326
        }
327
        if ($this->folderDstPath === null) {
328
            echo 'ERROR: Destiny folder not setted.' . \PHP_EOL;
329
            return self::RETURN_DST_FOLDER_NOT_SET;
330
        }
331
        if ($this->tagName === null) {
332
            echo 'ERROR: Tag name not setted.' . \PHP_EOL;
333
            return self::RETURN_TAG_NAME_NOT_SET;
334
        }
335
        if (!is_dir($this->folderSrcPath)) {
336
            echo 'ERROR: Source folder ' . $this->folderSrcPath . ' not exists.' . \PHP_EOL;
337
            return self::RETURN_SRC_FOLDER_NOT_EXISTS;
338
        }
339
        if (!is_file($this->folderDstPath) && !@mkdir($this->folderDstPath) && !is_dir($this->folderDstPath)) {
340
            echo "ERROR: Can't create folder " . $this->folderDstPath;
341
            return self::RETURN_CANT_CREATE_FOLDER;
342
        }
343
        return self::RETURN_SUCCESS;
344
    }
345
346
    /**
347
     * Custom function to re-order with usort.
348
     *
349
     * @param SimpleXMLElement $xmlA
350
     * @param SimpleXMLElement $xmlB
351
     *
352
     * @return int
353
     */
354
    private function sortName(SimpleXMLElement $xmlA, SimpleXMLElement $xmlB): int
355
    {
356
        return strcasecmp($xmlA->{$this->tagName}, $xmlB->{$this->tagName});
357
    }
358
359
    /**
360
     * Generate the content of the XML.
361
     *
362
     * @param array|object $data
363
     * @param string       $tagName
364
     *
365
     * @return string
366
     */
367
    private function generateXMLContent($data, $tagName = 'column'): string
368
    {
369
        $str = '';
370
        if (\is_array($data)) {
371
            foreach ($data as $field) {
372
                $str .= '    <' . $tagName . '>' . PHP_EOL;
373
                foreach ((array) $field as $key => $value) {
374
                    $str .= '        <' . $key . '>' . $value . '</' . $key . '>' . PHP_EOL;
375
                }
376
                $str .= '    </' . $tagName . '>' . PHP_EOL;
377
            }
378
        }
379
        if (\is_object($data)) {
380
            $str .= '    <' . $tagName . '>' . PHP_EOL;
381
            foreach ((array) $data as $key => $value) {
382
                $str .= '        <' . $key . '>' . $value . '</' . $key . '>' . PHP_EOL;
383
            }
384
            $str .= '    </' . $tagName . '>' . PHP_EOL;
385
        }
386
387
        return $str;
388
    }
389
390
    /**
391
     * Generate the header of the XML.
392
     *
393
     * @param string $fileName
394
     *
395
     * @return string
396
     */
397
    private function generateXMLHeader(string $fileName): string
398
    {
399
        $str = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL;
400
        $str .= '<!--' . PHP_EOL;
401
        $str .= '    Document   : ' . $fileName . PHP_EOL;
402
        $str .= '    Author     : Ordered with OrderXmlTables from FSConsoleTools' . PHP_EOL;
403
        $str .= '    Description: Structure for the ' . str_replace('.xml', '', $fileName) . ' table.' . PHP_EOL;
404
        $str .= '-->' . PHP_EOL;
405
        return $str;
406
    }
407
}
408