Completed
Push — master ( 31b5db...524ecf )
by Michael
03:26
created

Creator   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 6
Bugs 0 Features 2
Metric Value
wmc 24
c 6
b 0
f 2
lcom 1
cbo 5
dl 0
loc 226
ccs 0
cts 160
cp 0
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
C createXsd() 0 69 8
B inferTypeFromName() 0 25 5
B processRowset() 0 36 5
A processValueOnly() 0 23 3
A getTidy() 0 15 2
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains Creator class.
5
 *
6
 * PHP version 7.0+
7
 *
8
 * LICENSE:
9
 * This file is part of Yet Another Php Eve Api Library also know as Yapeal which can be used to access the Eve Online
10
 * API data and place it into a database.
11
 * Copyright (C) 2015-2016 Michael Cummings
12
 *
13
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
14
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option)
15
 * any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
18
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
19
 * details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public License along with this program. If not, see
22
 * <http://spdx.org/licenses/LGPL-3.0.html>.
23
 *
24
 * You should be able to find a copy of this license in the LICENSE.md file. A copy of the GNU GPL should also be
25
 * available in the GNU-GPL.md file.
26
 *
27
 * @copyright 2015-2016 Michael Cummings
28
 * @license   http://www.gnu.org/copyleft/lesser.html GNU LGPL
29
 * @author    Michael Cummings <[email protected]>
30
 */
31
namespace Yapeal\Xsd;
32
33
use Yapeal\Console\Command\EveApiCreatorTrait;
34
use Yapeal\Event\EveApiEventInterface;
35
use Yapeal\Event\MediatorInterface;
36
use Yapeal\Log\Logger;
37
38
/**
39
 * Class Creator
40
 */
41
class Creator
42
{
43
    use EveApiCreatorTrait;
44
    /**
45
     * Creator constructor.
46
     *
47
     * @param \Twig_Environment $twig
48
     * @param string            $dir
49
     */
50
    public function __construct(\Twig_Environment $twig, string $dir = __DIR__)
51
    {
52
        $this->setDir($dir);
53
        $this->setTwig($twig);
54
    }
55
    /**
56
     * @param EveApiEventInterface $event
57
     * @param string               $eventName
58
     * @param MediatorInterface    $yem
59
     *
60
     * @return EveApiEventInterface
61
     * @throws \DomainException
62
     * @throws \InvalidArgumentException
63
     * @throws \LogicException
64
     */
65
    public function createXsd(
66
        EveApiEventInterface $event,
67
        string $eventName,
68
        MediatorInterface $yem
69
    ): EveApiEventInterface
70
    {
71
        $this->setYem($yem);
72
        $data = $event->getData();
73
        $yem->triggerLogEvent('Yapeal.Log.log',
74
                Logger::DEBUG,
75
                $this->getReceivedEventMessage($data, $eventName, __CLASS__));
76
        // Only work with raw unaltered XML data.
77
        if (false !== strpos($data->getEveApiXml(), '<?yapeal.parameters.json')) {
78
            return $event->setHandledSufficiently();
79
        }
80
        $outputFile = sprintf('%1$s%2$s/%3$s.xsd',
81
            $this->getDir(),
82
            $data->getEveApiSectionName(),
83
            $data->getEveApiName());
84
        // Nothing to do if NOT overwriting and file exists.
85
        if (false === $this->isOverwrite() && is_file($outputFile)) {
86
            return $event;
87
        }
88
        $this->sectionName = $data->getEveApiSectionName();
89
        $xml = $data->getEveApiXml();
90
        if (false === $xml) {
91
            return $event->setHandledSufficiently();
92
        }
93
        $sxi = new \SimpleXMLIterator($xml);
94
        $this->tables = [];
95
        $this->processValueOnly($sxi, lcfirst($data->getEveApiName()));
96
        $this->processRowset($sxi);
97
        list($mSec, $sec) = explode(' ', microtime());
98
        $vars = [
99
            'className' => lcfirst($data->getEveApiName()),
100
            'tables' => $this->tables,
101
            'sectionName' => lcfirst($this->sectionName),
102
            'version' => gmdate('YmdHis', $sec) . sprintf('.%0-3s', floor($mSec * 1000))
103
        ];
104
        $templateName = $this->getTemplateName('xsd',
105
            ucfirst($this->sectionName),
106
            $data->getEveApiName());
107
        if (false === $templateName) {
108
            $mess ='Failed to find usable xsd template file during';
109
            $yem->triggerLogEvent('Yapeal.Log.log',
110
                Logger::WARNING,
111
                $this->createEventMessage($mess, $data, $eventName));
112
            return $event;
113
        }
114
        try {
115
            $contents = $this->getTwig()
116
                ->render($templateName, $vars);
117
        } catch (\Twig_Error $exc) {
118
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::ERROR, 'Twig error', ['exception' => $exc]);
119
            $yem->triggerLogEvent('Yapeal.Log.log',
120
                    Logger::WARNING,
121
                    $this->getFailedToWriteFile($data, $eventName, $outputFile));
122
            return $event;
123
        }
124
        $contents = $this->getTidy()
125
            ->repairString($contents);
126
        if (false === $this->safeFileWrite($contents, $outputFile, $this->getYem())) {
127
            $yem->triggerLogEvent($eventName,
128
                    Logger::WARNING,
129
                    $this->getFailedToWriteFile($data, $eventName, $outputFile));
130
            return $event;
131
        }
132
        return $event->setHandledSufficiently();
133
    }
134
    /**
135
     * Used to infer(choose) type from element or attribute's name.
136
     *
137
     * @param string $name Name of the element or attribute.
138
     * @param bool   $forValue Determines if returned type is going to be used for element or an attribute.
139
     *
140
     * @return string Returns the inferred type from the name.
141
     */
142
    private function inferTypeFromName(string $name, bool $forValue = false): string
143
    {
144
        if ('ID' === substr($name, -2)) {
145
            return 'eveIDType';
146
        }
147
        $name = strtolower($name);
148
        foreach ([
149
                     'descr' => 'xs:string',
150
                     'name' => 'eveNameType',
151
                     'balance' => 'eveISKType',
152
                     'isk' => 'eveISKType',
153
                     'tax' => 'eveISKType',
154
                     'timeefficiency' => 'xs:unsignedByte',
155
                     'date' => 'eveNEDTType',
156
                     'time' => 'eveNEDTType',
157
                     'until' => 'eveNEDTType',
158
                     'errorcode' => 'xs:unsignedShort',
159
                     'level' => 'xs:unsignedShort'
160
                 ] as $search => $replace) {
161
            if (false !== strpos($name, $search)) {
162
                return $replace;
163
            }
164
        }
165
        return $forValue ? 'xs:string' : 'xs:token';
166
    }
167
    /**
168
     * @param \SimpleXMLIterator $sxi
169
     * @param string             $xpath
170
     */
171
    protected function processRowset(\SimpleXMLIterator $sxi, string $xpath = '//result/rowset')
172
    {
173
        $items = $sxi->xpath($xpath);
174
        if (0 === count($items)) {
175
            return;
176
        }
177
        $tables = [];
178
        foreach ($items as $ele) {
179
            $tableName = (string)$ele['name'];
180
            /**
181
             * @var string[] $colNames
182
             */
183
            $colNames = explode(',', (string)$ele['columns']);
184
            /**
185
             * @var string[] $keyNames
186
             */
187
            $keyNames = explode(',', (string)$ele['key']);
188
            $columns = [];
189
            foreach ($keyNames as $keyName) {
190
                $columns[$keyName] = $this->inferTypeFromName($keyName);
191
            }
192
            foreach ($colNames as $colName) {
193
                $columns[$colName] = $this->inferTypeFromName($colName);
194
            }
195
            uksort($columns,
196
                function ($alpha, $beta) {
197
                    return strtolower($alpha) <=> strtolower($beta);
198
                });
199
            $tables[$tableName] = ['attributes' => $columns];
200
        }
201
        uksort($tables,
202
            function ($alpha, $beta) {
203
                return strtolower($alpha) <=> strtolower($beta);
204
            });
205
        $this->tables = array_merge($this->tables, $tables);
206
    }
207
    /**
208
     * @param \SimpleXMLIterator $sxi
209
     *
210
     * @param string             $tableName
211
     * @param string             $xpath
212
     */
213
    protected function processValueOnly(
214
        \SimpleXMLIterator $sxi,
215
        string $tableName,
216
        string $xpath = '//result/child::*[not(*|@*|self::dataTime)]'
217
    ) {
218
        $items = $sxi->xpath($xpath);
219
        if (0 === count($items)) {
220
            return;
221
        }
222
        $columns = [];
223
        /**
224
         * @var \SimpleXMLElement $ele
225
         */
226
        foreach ($items as $ele) {
227
            $name = (string)$ele->getName();
228
            $columns[$name] = $this->inferTypeFromName($name, true);
229
        }
230
        uksort($columns,
231
            function ($alpha, $beta) {
232
                return strtolower($alpha) <=> strtolower($beta);
233
            });
234
        $this->tables[$tableName] = ['values' => $columns];
235
    }
236
    /**
237
     * @return \tidy
238
     */
239
    private function getTidy(): \tidy
240
    {
241
        if (null === $this->tidy) {
242
            $tidyConfig = [
243
                'indent' => true,
244
                'indent-spaces' => 4,
245
                'input-xml' => true,
246
                'newline' => 'LF',
247
                'output-xml' => true,
248
                'wrap' => '120'
249
            ];
250
            $this->tidy = new \tidy(null, $tidyConfig, 'utf8');
251
        }
252
        return $this->tidy;
253
    }
254
    /**
255
     * @var string $sectionName
256
     */
257
    private $sectionName;
258
    /**
259
     * @var array $tables
260
     */
261
    private $tables;
262
    /**
263
     * @var \tidy $tidy
264
     */
265
    private $tidy;
266
}
267