Completed
Push — master ( eaf5ea...97c539 )
by Michael
05:35
created

Transformer   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 204
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 2.3%

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 6
dl 0
loc 204
ccs 2
cts 87
cp 0.023
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B transformEveApi() 0 34 3
A addYapealProcessingInstructionToXml() 0 22 3
A checkLibXmlErrors() 0 10 3
A getXslt() 0 7 2
B performTransform() 0 63 7
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains Transformer 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
10
 * which can be used to access the Eve Online API data and place it into a
11
 * database.
12
 * Copyright (C) 2015-2016 Michael Cummings
13
 *
14
 * This program is free software: you can redistribute it and/or modify it
15
 * under the terms of the GNU Lesser General Public License as published by the
16
 * Free Software Foundation, either version 3 of the License, or (at your
17
 * option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful, but WITHOUT
20
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
22
 * for more details.
23
 *
24
 * You should have received a copy of the GNU Lesser General Public License
25
 * along with this program. If not, see
26
 * <http://spdx.org/licenses/LGPL-3.0.html>.
27
 *
28
 * You should be able to find a copy of this license in the COPYING-LESSER.md
29
 * file. A copy of the GNU GPL should also be available in the COPYING.md file.
30
 *
31
 * @copyright 2015-2016 Michael Cummings
32
 * @license   http://www.gnu.org/copyleft/lesser.html GNU LGPL
33
 * @author    Michael Cummings <[email protected]>
34
 */
35
namespace Yapeal\Xsl;
36
37
use Yapeal\Event\EveApiEventEmitterTrait;
38
use Yapeal\Event\EveApiEventInterface;
39
use Yapeal\Event\MediatorInterface;
40
use Yapeal\Exception\YapealFileSystemException;
41
use Yapeal\FileSystem\RelativeFileSearchTrait;
42
use Yapeal\FileSystem\SafeFileHandlingTrait;
43
use Yapeal\Log\Logger;
44
use Yapeal\Xml\EveApiReadWriteInterface;
45
46
/**
47
 * Class Transformer
48
 */
49
class Transformer implements TransformerInterface
50
{
51
    use EveApiEventEmitterTrait;
52
    use RelativeFileSearchTrait;
53
    use SafeFileHandlingTrait;
54
    /**
55
     * Transformer Constructor.
56 1
     *
57
     * @param string $dir Base directory where Eve API XSL files can be found.
58 1
     */
59
    public function __construct(string $dir = __DIR__)
60
    {
61
        $this->setRelativeBaseDir($dir . '/');
62
    }
63
    /**
64
     * @param EveApiEventInterface $event
65
     * @param string               $eventName
66
     * @param MediatorInterface    $yem
67
     *
68
     * @return EveApiEventInterface
69
     * @throws \DomainException
70
     * @throws \InvalidArgumentException
71
     * @throws \LogicException
72
     */
73
    public function transformEveApi(EveApiEventInterface $event, string $eventName, MediatorInterface $yem)
74
    {
75
        $this->setYem($yem);
76
        $data = $event->getData();
77
        $yem->triggerLogEvent('Yapeal.Log.log',
78
            Logger::DEBUG,
79
            $this->getReceivedEventMessage($data, $eventName, __CLASS__));
80
        try {
81
            $xslName = $this->findRelativeFileWithPath(ucfirst($data->getEveApiSectionName()),
82
                $data->getEveApiName(),
83
                'xsl');
84
        } catch (YapealFileSystemException $exc) {
85
            $mess = 'Failed to find accessible XSL transform file during';
86
            $yem->triggerLogEvent('Yapeal.Log.log',
87
                Logger::WARNING,
88
                $this->createEventMessage($mess, $data, $eventName),
89
                ['exception' => $exc]);
90
            return $event;
91
        }
92
        $mess = sprintf('Using %s file to transform', $xslName);
93
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $this->createEveApiMessage($mess, $data));
94
        $xml = $this->addYapealProcessingInstructionToXml($data)
95
            ->performTransform($xslName, $data);
96
        if (false === $xml) {
97
            $mess = 'Failed to transform xml during';
98
            $yem->triggerLogEvent('Yapeal.Log.log',
99
                Logger::WARNING,
100
                $this->createEventMessage($mess, $data, $eventName));
101
            return $event;
102
        }
103
        $xml = (new \tidy())->repairString($xml, $this->tidyConfig, 'utf8');
104
        return $event->setData($data->setEveApiXml($xml))
105
            ->setHandledSufficiently();
106
    }
107
    /**
108
     * Adds Processing Instruction to XML containing json encoding of any post used during retrieve.
109
     *
110
     * NOTE: This use to be done directly in the network retriever but felt modifying the XML like that belonged in
111
     * transform instead.
112
     *
113
     * @param EveApiReadWriteInterface $data
114
     *
115
     * @return self Fluent interface.
116
     * @throws \InvalidArgumentException
117
     * @throws \LogicException
118
     */
119
    private function addYapealProcessingInstructionToXml(EveApiReadWriteInterface $data): self
120
    {
121
        $xml = $data->getEveApiXml();
122
        if (false === $xml) {
123
            return $this;
124
        }
125
        $arguments = $data->getEveApiArguments();
126
        if (!empty($arguments['vCode'])) {
127
            $arguments['vCode'] = substr($arguments['vCode'], 0, 8) . '...';
128
        }
129
        unset($arguments['mask'], $arguments['rowCount']);
130
        ksort($arguments);
131
        $json = json_encode($arguments);
132
        $xml = str_replace(["encoding='UTF-8'?>\r\n<eveapi", "encoding='UTF-8'?>\n<eveapi"],
133
            [
134
                "encoding='UTF-8'?>\r\n<?yapeal.parameters.json " . $json . "?>\r\n<eveapi",
135
                "encoding='UTF-8'?>\n<?yapeal.parameters.json " . $json . "?>\n<eveapi"
136
            ],
137
            $xml);
138
        $data->setEveApiXml($xml);
139
        return $this;
140
    }
141
    private function checkLibXmlErrors()
142
    {
143
        $errors = libxml_get_errors();
144
        if (0 !== count($errors)) {
145
            foreach ($errors as $error) {
146
                $this->getYem()
147
                    ->triggerLogEvent('Yapeal.Log.log', Logger::NOTICE, $error->message);
148
            }
149
        }
150
    }
151
    /**
152
     * @return \XSLTProcessor
153
     */
154
    private function getXslt(): \XSLTProcessor
155
    {
156
        if (null === $this->xslt) {
157
            $this->xslt = new \XSLTProcessor();
158
        }
159
        return $this->xslt;
160
    }
161
    /**
162
     * Does actual XSL transform on the Eve API XML.
163
     *
164
     * @param string                   $xslName
165
     * @param EveApiReadWriteInterface $data
166
     *
167
     * @return string|false
168
     * @throws \DomainException
169
     * @throws \InvalidArgumentException
170
     * @throws \LogicException
171
     */
172
    private function performTransform(string $xslName, EveApiReadWriteInterface $data)
173
    {
174
        $xsl = $this->safeFileRead($xslName);
175
        if (false === $xsl) {
176
            $mess = sprintf('Failed to read XSL file %s during', $xslName);
177
            $this->getYem()
178
                ->triggerLogEvent('Yapeal.Log.log', Logger::NOTICE, $this->createEveApiMessage($mess, $data));
179
            return false;
180
        }
181
        if ('' === $xsl) {
182
            $mess = sprintf('Received an empty XSL file %s during', $xslName);
183
            $this->getYem()
184
                ->triggerLogEvent('Yapeal.Log.log', Logger::NOTICE, $this->createEveApiMessage($mess, $data));
185
            return false;
186
        }
187
        libxml_clear_errors();
188
        libxml_use_internal_errors(true);
189
        libxml_clear_errors();
190
        $xslt = $this->getXslt();
191
        try {
192
            $result = $xslt->importStylesheet(new \SimpleXMLElement($xsl));
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $xslt->importStylesheet(...SimpleXMLElement($xsl)) (which targets XSLTProcessor::importStylesheet()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
193
        } catch (\Exception $exc) {
194
            $mess = sprintf('XSL file %s cause SimpleXMLElement exception during', $xslName);
195
            $this->getYem()
196
                ->triggerLogEvent('Yapeal.log.log',
197
                    Logger::WARNING,
198
                    $this->createEveApiMessage($mess, $data),
199
                    ['exception' => $exc]);
200
            $this->checkLibXmlErrors();
201
            return false;
202
        }
203
        if (false === $result) {
204
            $mess = sprintf('XSLT could not import %s style sheet during', $xslName);
205
            $this->getYem()
206
                ->triggerLogEvent('Yapeal.Log.log', Logger::NOTICE, $this->createEveApiMessage($mess, $data));
207
            $this->checkLibXmlErrors();
208
            return false;
209
        }
210
        $result = false;
211
        try {
212
            $result = $xslt->transformToXml(new \SimpleXMLElement($data->getEveApiXml()));
213
        } catch (\Exception $exc) {
214
            $mess = 'XML cause SimpleXMLElement exception in';
215
            $this->getYem()
216
                ->triggerLogEvent('Yapeal.log.log',
217
                    Logger::WARNING,
218
                    $this->createEveApiMessage($mess, $data),
219
                    ['exception' => $exc]);
220
            // Fall through intended so any libxml errors are logged and the untransformed XML can be preserved.
221
        }
222
        if (false === $result) {
223
            $this->checkLibXmlErrors();
224
            $apiName = $data->getEveApiName();
225
            $data->setEveApiName('Untransformed_' . $apiName);
226
            // Cache error causing XML.
227
            $this->emitEvents($data, 'preserve', 'Yapeal.Xml.Error');
228
            $data->setEveApiName($apiName);
229
        }
230
        libxml_clear_errors();
231
        libxml_use_internal_errors(false);
232
        libxml_clear_errors();
233
        return $result;
234
    }
235
    /**
236
     * Holds tidy config settings.
237
     *
238
     * @var array $tidyConfig
239
     */
240
    private $tidyConfig = [
241
        'indent' => true,
242
        'indent-spaces' => 4,
243
        'input-xml' => true,
244
        'newline' => 'LF',
245
        'output-xml' => true,
246
        'wrap' => '1000'
247
    ];
248
    /**
249
     * @var \XSLTProcessor $xslt
250
     */
251
    private $xslt;
252
}
253