Completed
Push — master ( df4948...3b59f8 )
by Michael
02:51
created

Creator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $b. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
253
            $a = strtolower($a);
254
            $b = strtolower($b);
255
            if ($a < $b) {
256
                return -1;
257
            } elseif ($a > $b) {
258
                return 1;
259
            }
260
            return 0;
261
        });
262
        $this->tables[$tableName] = ['values' => $columns];
263
    }
264
    /**
265
     * @var string $sectionName
266
     */
267
    protected $sectionName;
268
    /**
269
     * @var array $tables
270
     */
271
    protected $tables;
272
}
273