Passed
Push — master ( ff9e28...8752e2 )
by Francesc
01:52
created

OrderXmlTables::setSrcFolder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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