Completed
Push — master ( 4a905b...82874d )
by Michael
02:55
created

Creator   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 216
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 24
c 3
b 0
f 0
lcom 1
cbo 5
dl 0
loc 216
ccs 0
cts 146
cp 0
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
B createXsd() 0 54 7
B processRowset() 0 36 5
B processValueOnly() 0 23 4
A getTidy() 0 15 2
B inferTypeFromName() 0 25 5
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\Cli\EveApi\EveApiCreatorTrait;
34
use Yapeal\Event\EveApiEventInterface;
35
use Yapeal\Event\MediatorInterface;
36
use Yapeal\FileSystem\RelativeFileSearchTrait;
37
use Yapeal\Log\Logger;
38
39
/**
40
 * Class Creator
41
 */
42
class Creator
43
{
44
    use EveApiCreatorTrait, RelativeFileSearchTrait;
45
    /**
46
     * Creator constructor.
47
     *
48
     * @param \Twig_Environment $twig
49
     * @param string            $dir
50
     */
51
    public function __construct(\Twig_Environment $twig, string $dir = __DIR__)
52
    {
53
        $this->setRelativeBaseDir($dir);
54
        $this->setTwig($twig);
55
        $this->twigExtension = 'xsd.twig';
56
    }
57
    /**
58
     * @param EveApiEventInterface $event
59
     * @param string               $eventName
60
     * @param MediatorInterface    $yem
61
     *
62
     * @return EveApiEventInterface
63
     * @throws \DomainException
64
     * @throws \InvalidArgumentException
65
     * @throws \LogicException
66
     */
67
    public function createXsd(
68
        EveApiEventInterface $event,
69
        string $eventName,
70
        MediatorInterface $yem
71
    ): EveApiEventInterface
72
    {
73
        $this->setYem($yem);
74
        $data = $event->getData();
75
        $yem->triggerLogEvent('Yapeal.Log.log',
76
            Logger::DEBUG,
77
            $this->getReceivedEventMessage($data, $eventName, __CLASS__));
78
        // Only work with raw unaltered XML data.
79
        if (false !== strpos($data->getEveApiXml(), '<?yapeal.parameters.json')) {
80
            return $event->setHandledSufficiently();
81
        }
82
        $this->sectionName = $data->getEveApiSectionName();
83
        $this->apiName = $data->getEveApiName();
84
        $outputFile = sprintf('%1$s%2$s/%3$s.xsd',
85
            $this->getRelativeBaseDir(),
86
            ucfirst($this->sectionName),
87
            ucfirst($this->apiName));
88
        // Nothing to do if NOT overwriting and file exists.
89
        if (false === $this->isOverwrite() && is_file($outputFile)) {
90
            return $event;
91
        }
92
        $xml = $data->getEveApiXml();
93
        if (false === $xml) {
94
            return $event->setHandledSufficiently();
95
        }
96
        $sxi = new \SimpleXMLIterator($xml);
97
        $this->tables = [];
98
        $this->processValueOnly($sxi, lcfirst($this->apiName));
99
        $this->processRowset($sxi);
100
        list($mSec, $sec) = explode(' ', microtime());
101
        $context = [
102
            'className' => lcfirst($this->apiName),
103
            'tables' => $this->tables,
104
            'sectionName' => lcfirst($this->sectionName),
105
            'version' => gmdate('YmdHis', $sec) . sprintf('.%0-3s', floor($mSec * 1000))
106
        ];
107
        $contents = $this->getContentsFromTwig($eventName, $data, $context);
108
        if (false === $contents) {
109
            return $event;
110
        }
111
        $contents = $this->getTidy()
112
            ->repairString($contents);
113
        if (false === $this->safeFileWrite($contents, $outputFile)) {
114
            $yem->triggerLogEvent($eventName,
115
                Logger::WARNING,
116
                $this->getFailedToWriteFileMessage($data, $eventName, $outputFile));
117
            return $event;
118
        }
119
        return $event->setHandledSufficiently();
120
    }
121
    /**
122
     * @param \SimpleXMLIterator $sxi
123
     * @param string             $xpath
124
     */
125
    protected function processRowset(\SimpleXMLIterator $sxi, string $xpath = '//result/rowset')
126
    {
127
        $items = $sxi->xpath($xpath);
128
        if (0 === count($items)) {
129
            return;
130
        }
131
        $tables = [];
132
        foreach ($items as $ele) {
133
            $tableName = (string)$ele['name'];
134
            /**
135
             * @var string[] $colNames
136
             */
137
            $colNames = explode(',', (string)$ele['columns']);
138
            /**
139
             * @var string[] $keyNames
140
             */
141
            $keyNames = explode(',', (string)$ele['key']);
142
            $columns = [];
143
            foreach ($keyNames as $keyName) {
144
                $columns[$keyName] = $this->inferTypeFromName($keyName);
145
            }
146
            foreach ($colNames as $colName) {
147
                $columns[$colName] = $this->inferTypeFromName($colName);
148
            }
149
            uksort($columns,
150
                function ($alpha, $beta) {
151
                    return strtolower($alpha) <=> strtolower($beta);
152
                });
153
            $tables[$tableName] = ['attributes' => $columns];
154
        }
155
        uksort($tables,
156
            function ($alpha, $beta) {
157
                return strtolower($alpha) <=> strtolower($beta);
158
            });
159
        $this->tables = array_merge($this->tables, $tables);
160
    }
161
    /**
162
     * @param \SimpleXMLIterator $sxi
163
     *
164
     * @param string             $tableName
165
     * @param string             $xpath
166
     */
167
    protected function processValueOnly(
168
        \SimpleXMLIterator $sxi,
169
        string $tableName,
170
        string $xpath = '//result/child::*[not(*|@*|self::dataTime)]'
171
    ) {
172
        $items = $sxi->xpath($xpath);
173
        if (false === $items || 0 === count($items)) {
174
            return;
175
        }
176
        /**
177
         * @var \SimpleXMLElement[] $items
178
         */
179
        $columns = [];
180
        foreach ($items as $ele) {
181
            $name = (string)$ele->getName();
182
            $columns[$name] = $this->inferTypeFromName($name, true);
183
        }
184
        uksort($columns,
185
            function ($alpha, $beta) {
186
                return strtolower($alpha) <=> strtolower($beta);
187
            });
188
        $this->tables[$tableName] = ['values' => $columns];
189
    }
190
    /**
191
     * @return \tidy
192
     */
193
    private function getTidy(): \tidy
194
    {
195
        if (null === $this->tidy) {
196
            $tidyConfig = [
197
                'indent' => true,
198
                'indent-spaces' => 4,
199
                'input-xml' => true,
200
                'newline' => 'LF',
201
                'output-xml' => true,
202
                'wrap' => '120'
203
            ];
204
            $this->tidy = new \tidy(null, $tidyConfig, 'utf8');
205
        }
206
        return $this->tidy;
207
    }
208
    /**
209
     * Used to infer(choose) type from element or attribute's name.
210
     *
211
     * @param string $name     Name of the element or attribute.
212
     * @param bool   $forValue Determines if returned type is going to be used for element or an attribute.
213
     *
214
     * @return string Returns the inferred type from the name.
215
     */
216
    private function inferTypeFromName(string $name, bool $forValue = false): string
217
    {
218
        if ('ID' === substr($name, -2)) {
219
            return 'eveIDType';
220
        }
221
        $name = strtolower($name);
222
        foreach ([
223
                     'descr' => 'xs:string',
224
                     'name' => 'eveNameType',
225
                     'balance' => 'eveISKType',
226
                     'isk' => 'eveISKType',
227
                     'tax' => 'eveISKType',
228
                     'timeefficiency' => 'xs:unsignedByte',
229
                     'date' => 'eveNEDTType',
230
                     'time' => 'eveNEDTType',
231
                     'until' => 'eveNEDTType',
232
                     'errorcode' => 'xs:unsignedShort',
233
                     'level' => 'xs:unsignedShort'
234
                 ] as $search => $replace) {
235
            if (false !== strpos($name, $search)) {
236
                return $replace;
237
            }
238
        }
239
        return $forValue ? 'xs:string' : 'xs:token';
240
    }
241
    /**
242
     * @var string $apiName
243
     */
244
    private $apiName;
245
    /**
246
     * @var string $sectionName
247
     */
248
    private $sectionName;
249
    /**
250
     * @var array $tables
251
     */
252
    private $tables;
253
    /**
254
     * @var \tidy $tidy
255
     */
256
    private $tidy;
257
}
258