Passed
Pull Request — master (#207)
by
unknown
16:03 queued 05:02
created

ParserGenerator   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 415
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 38
eloc 176
dl 0
loc 415
rs 9.36
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 25 3
A getXMLData() 0 6 1
A setXML() 0 6 2
C buildXmlFromForm() 0 74 13
D customXPath() 0 186 17
A parseXPath() 0 7 1
A parseXPathWrapped() 0 8 1
1
<?php
2
namespace EWW\Dpf\Services;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use EWW\Dpf\Configuration\ClientConfigurationManager;
18
use EWW\Dpf\Domain\Repository\DocumentTypeRepository;
19
use EWW\Dpf\Helper\XSLTransformator;
20
use TYPO3\CMS\Extbase\Object\ObjectManager;
21
use EWW\Dpf\Services\Transformer\DocumentTransformer;
22
23
/**
24
 * ParserGenerator
25
 */
26
class ParserGenerator
27
{
28
    /**
29
     * clientConfigurationManager
30
     *
31
     * @var \EWW\Dpf\Configuration\ClientConfigurationManager
32
     * @TYPO3\CMS\Extbase\Annotation\Inject
33
     */
34
    protected $clientConfigurationManager;
35
36
    /**
37
     * documentTypeRepository
38
     *
39
     * @var \EWW\Dpf\Domain\Repository\DocumentTypeRepository
40
     */
41
    protected $documentTypeRepository = null;
42
43
    /**
44
     * formData
45
     *
46
     * @var array
47
     */
48
    protected $formData = array();
49
50
    /**
51
     * files from form
52
     * @var array
53
     */
54
    protected $files = array();
55
56
    /**
57
     * metsData
58
     *
59
     * @var  DOMDocument
0 ignored issues
show
Bug introduced by
The type EWW\Dpf\Services\DOMDocument was not found. Did you mean DOMDocument? If so, make sure to prefix the type with \.
Loading history...
60
     */
61
    protected $metsData = '';
62
63
    /**
64
     * xml data
65
     * @var DOMDocument
66
     */
67
    protected $xmlData = '';
68
69
    /**
70
     * xml header
71
     * @var string
72
     */
73
    protected $xmlHeader = '';
74
75
    /**
76
     * xPathXMLGenerator
77
     * @var object
78
     */
79
    protected $parser = null;
80
81
    /**
82
     * ref id counter
83
     */
84
    protected $counter = 0;
85
86
    /**
87
     * namespaces as string
88
     * @var string
89
     */
90
    protected $namespaceString = '';
91
92
93
    /**
94
     * Constructor
95
     */
96
    public function __construct()
97
    {
98
        $objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class);
99
        $this->clientConfigurationManager = $objectManager->get(ClientConfigurationManager::class);
100
101
        $this->documentTypeRepository = $objectManager->get(DocumentTypeRepository::class);
102
103
        $namespaceConfigurationString = $this->clientConfigurationManager->getNamespaces();
104
        if (!empty($namespaceConfigurationString)) {
105
                        $namespaceConfiguration = explode(";", $namespaceConfigurationString);
106
                        foreach ($namespaceConfiguration as $value) {
107
                                $namespace = explode("=", $value);
108
                                $this->namespaceString .= ' xmlns:' . $namespace[0] . '="' . $namespace[1] . '"';
109
                            }
110
         }
111
112
        $this->xmlHeader = '<data' . $this->namespaceString . '></data>';
113
114
        $this->xmlData =  new \DOMDocument();
0 ignored issues
show
Documentation Bug introduced by
It seems like new DOMDocument() of type DOMDocument is incompatible with the declared type EWW\Dpf\Services\DOMDocument of property $xmlData.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
115
        $this->xmlData->loadXML($this->xmlHeader);
116
117
        // Parser
118
        include_once 'XPathXMLGenerator.php';
119
120
        $this->parser = new XPathXMLGenerator();
121
    }
122
123
    /**
124
     * returns the mods xml string
125
     * @return string mods xml
126
     */
127
    public function getXMLData()
128
    {
129
        $xml = $this->xmlData->saveXML();
130
        $xml = preg_replace("/eww=\"\d-\d-\d\"/", '${1}${2}${3}', $xml);
131
132
        return $xml;
133
    }
134
135
    /**
136
     * build mods from form array
137
     * @param array $array structured form data array
138
     */
139
    public function buildXmlFromForm($array)
140
    {
141
        $fedoraNamespace = stream_copy_to_stream($this->clientConfigurationManager->getFedoraNamespace());
0 ignored issues
show
Bug introduced by
The call to stream_copy_to_stream() has too few arguments starting with to. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

141
        $fedoraNamespace = /** @scrutinizer ignore-call */ stream_copy_to_stream($this->clientConfigurationManager->getFedoraNamespace());

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
142
143
        $this->xmlData = $this->xmlData;
144
        // Build xml mods from form fields
145
        // loop each group
146
        foreach ($array['metadata'] as $key => $group) {
147
            //groups
148
            $mapping = $group['mapping'];
149
150
            $values     = $group['values'];
151
            $attributes = $group['attributes'];
152
153
            $attributeXPath     = '';
154
            $extensionAttribute = '';
155
            foreach ($attributes as $attribute) {
156
                if (!$attribute["modsExtension"]) {
157
                    $attributeXPath .= '[' . $attribute['mapping'] . '="' . $attribute['value'] . '"]';
158
                } else {
159
                    $extensionAttribute .= '[' . $attribute['mapping'] . '="' . $attribute['value'] . '"]';
160
                }
161
162
            }
163
164
            // mods extension
165
            if ($group['modsExtensionMapping']) {
166
                $counter = sprintf("%'03d", $this->counter);
167
                $attributeXPath .= '[@ID="'.$fedoraNamespace.'_' . $counter . '"]';
168
            }
169
170
            $existsExtensionFlag = false;
171
            $i                   = 0;
172
            // loop each object
173
            if (!empty($values)) {
174
                //$values = empty($values)? [] : $values;
175
                foreach ($values as $value) {
176
177
                    if ($value['modsExtension']) {
178
                        $existsExtensionFlag = true;
179
                        // mods extension
180
                        $counter = sprintf("%'03d", $this->counter);
181
                        $referenceAttribute = $extensionAttribute . '[@' . $group['modsExtensionReference'] . '="' . $fedoraNamespace . '_' . $counter . '"]';
182
183
                        $path = $group['modsExtensionMapping'] . $referenceAttribute . '%/' . $value['mapping'];
184
185
                        $xml = $this->customXPath($path, false, $value['value']);
0 ignored issues
show
Bug introduced by
$path of type string is incompatible with the type EWW\Dpf\Services\xpath expected by parameter $xPath of EWW\Dpf\Services\ParserGenerator::customXPath(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

185
                        $xml = $this->customXPath(/** @scrutinizer ignore-type */ $path, false, $value['value']);
Loading history...
Unused Code introduced by
The assignment to $xml is dead and can be removed.
Loading history...
186
                    } else {
187
                        $path = $mapping . $attributeXPath . '%/' . $value['mapping'];
188
189
                        if ($i == 0) {
190
                            $newGroupFlag = true;
191
                        } else {
192
                            $newGroupFlag = false;
193
                        }
194
195
                        $this->customXPath($path, $newGroupFlag, $value['value']);
196
                        $i++;
197
198
                    }
199
200
                }
201
            } else {
202
                if (!empty($attributeXPath)) {
203
                    $path = $mapping . $attributeXPath;
204
                    $this->customXPath($path, true, '', true);
205
                }
206
            }
207
            if (!$existsExtensionFlag && $group['modsExtensionMapping']) {
208
                $xPath = $group['modsExtensionMapping'] . $extensionAttribute . '[@' . $group['modsExtensionReference'] . '="'.$fedoraNamespace.'_' . $counter . '"]';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $counter does not seem to be defined for all execution paths leading up to this point.
Loading history...
209
                $xml   = $this->customXPath($xPath, true, '', true);
210
            }
211
            if ($group['modsExtensionMapping']) {
212
                $this->counter++;
213
            }
214
        }
215
    }
216
217
    /**
218
     * get xml from xpath
219
     * @param  xpath $xPath xPath expression
220
     * @return xml
0 ignored issues
show
Bug introduced by
The type EWW\Dpf\Services\xml 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...
221
     */
222
    public function parseXPath($xPath)
223
    {
224
225
        $this->parser->generateXmlFromXPath($xPath);
226
        $xml = $this->parser->getXML();
227
228
        return $xml;
229
    }
230
231
    public function parseXPathWrapped($xPath)
232
    {
233
        $this->parser->generateXmlFromXPath($xPath);
234
        $xml = $this->parser->getXML();
235
236
        $xml = '<data' . $this->namespaceString . '>' . $xml . '</data>';
237
238
        return $xml;
239
    }
240
241
    /**
242
     * Customized xPath parser
243
     * @param  xpath  $xPath xpath expression
244
     * @param  string $value form value
245
     * @return xml    created xml
246
     */
247
    public function customXPath($xPath, $newGroupFlag = false, $value = '', $attributeOnly = false)
248
    {
249
        if (!$attributeOnly) {
250
            // Explode xPath
251
            $newPath = explode('%', $xPath);
252
253
            $praedicateFlag = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $praedicateFlag is dead and can be removed.
Loading history...
254
            $explodedXPath  = explode('[', $newPath[0]);
255
            if (count($explodedXPath) > 1) {
256
                // praedicate is given
257
                if (substr($explodedXPath[1], 0, 1) == "@") {
258
                    // attribute
259
                    $path = $newPath[0];
260
                } else {
261
                    // path
262
                    $path = $explodedXPath[0];
263
                }
264
265
                $praedicateFlag = true;
266
            } else {
267
                $path = $newPath[0];
268
            }
269
270
            if (!empty($value)) {
271
                $newPath[1] = $newPath[1] . '="' . $value . '"';
272
            }
273
274
            $modsDataXPath = \EWW\Dpf\Helper\XPath::create($this->xmlData);
275
276
            if (!$newGroupFlag && $modsDataXPath->query('/data/' . $newPath[0])->length > 0) {
277
                // first xpath path exist
278
279
                // build xml from second xpath part
280
                $xml = $this->parseXPath($newPath[1]);
281
282
                // check if xpath [] are nested
283
                $search = '/(\/\w*:\w*)\[(.*)\]/';
284
                preg_match($search, $newPath[1], $match);
285
                preg_match($search, $match[2], $secondMatch);
286
                // first part nested xpath
287
                if ($match[2] && $secondMatch[2]) {
288
                    $nested = $match[2];
289
290
                    $nestedXml = $this->parseXPath($nested);
291
292
                    // object xpath without nested element []
293
                    $newPath[1] = str_replace('['.$match[2].']', '', $newPath[1]);
294
295
                    $xml = $this->parseXPath($newPath[1]);
296
297
                }
298
299
                // FIXME: XPATHXmlGenerator XPATH does not generate any namespaces,
300
                // which DOMDocument cannot cope with. Actually, namespaces should not be necessary here,
301
                // since it is about child elements that are then added to the overall XML.
302
                libxml_use_internal_errors(true);
303
                $docXML = new \DOMDocument();
304
                $docXML->loadXML($xml);
305
                libxml_use_internal_errors(false);
306
307
                $domXPath = \EWW\Dpf\Helper\XPath::create($this->xmlData);
308
309
                // second part nested xpath
310
                if ($match[2] && $secondMatch[2]) {
311
312
                    // import node from nested
313
                    // FIXME: XPATHXmlGenerator XPATH does not generate any namespaces,
314
                    // which DOMDocument cannot cope with. Actually, namespaces should not be necessary here,
315
                    // since it is about child elements that are then added to the overall XML.
316
                    libxml_use_internal_errors(true);
317
                    $docXMLNested = new \DOMDocument();
318
                    $docXMLNested->loadXML($nestedXml);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $nestedXml does not seem to be defined for all execution paths leading up to this point.
Loading history...
319
                    libxml_use_internal_errors(false);
320
321
                    $xPath = \EWW\Dpf\Helper\XPath::create($docXML);
322
323
                    $nodeList = $xPath->query($match[1]);
324
                    $node = $nodeList->item(0);
325
326
                    $importNode = $docXML->importNode($docXMLNested->getElementsByTagName("mods")->item(0)->firstChild, true);
0 ignored issues
show
Bug introduced by
It seems like $docXMLNested->getElemen...')->item(0)->firstChild can also be of type null; however, parameter $node of DOMDocument::importNode() does only seem to accept DOMNode, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

326
                    $importNode = $docXML->importNode(/** @scrutinizer ignore-type */ $docXMLNested->getElementsByTagName("mods")->item(0)->firstChild, true);
Loading history...
327
328
                    $node->appendChild($importNode);
329
                }
330
331
                $domNode = $domXPath->query('/data/' . $path);
332
                $node = $docXML->documentElement;
333
334
                $nodeAppendModsData = $this->xmlData->importNode($node, true);
335
                $domNode->item($domNode->length - 1)->appendChild($nodeAppendModsData);
336
            } else {
337
                // first xpath doesn't exist
338
                // parse first xpath part
339
                $xml1 = $this->parseXPathWrapped($newPath[0]);
340
341
                $doc1 = new \DOMDocument();
342
                $doc1->loadXML($xml1);
343
344
                $domXPath = \EWW\Dpf\Helper\XPath::create($doc1);
345
346
                $domNode = $domXPath->query('//' . $path);
347
348
                // parse second xpath part
349
                $xml2 = $this->parseXPathWrapped($path . $newPath[1]);
350
351
                // check if xpath [] are nested
352
                $search = '/(\/\w*:?\w*)\[(.*)\]/';
353
                preg_match($search, $newPath[1], $match);
354
                preg_match($search, $match[2], $secondMatch);
355
356
                // first part nested xpath
357
                if ($match[2] && $secondMatch[2]) {
358
                    $nested = $match[2];
359
360
                    $nestedXml = $this->parseXPathWrapped($nested);
361
362
                    // object xpath without nested element []
363
                    $newPath[1] = str_replace('['.$match[2].']', '', $newPath[1]);
364
365
                    $xml2 = $this->parseXPathWrapped($path . $newPath[1]);
366
                }
367
368
                $doc2 = new \DOMDocument();
369
                $doc2->loadXML($xml2);
370
371
                $domXPath2 = \EWW\Dpf\Helper\XPath::create($doc2);
372
373
                  // second part nested xpath
374
                if ($match[2] && $secondMatch[2]) {
375
                    // import node from nested
376
                    $docXMLNested = new \DOMDocument();
377
                    $docXMLNested->loadXML($nestedXml);
378
379
                    $xPath = \EWW\Dpf\Helper\XPath::create($doc2);
380
                    $nodeList = $xPath->query('//' . $path . $match[1]);
381
                    $node = $nodeList->item(0);
382
383
                    $importNode = $doc2->importNode($docXMLNested->documentElement, true);
384
385
                    $node->appendChild($importNode);
386
                }
387
                $domNode2 = $domXPath2->query('//' . $path)->item(0)->childNodes->item(0);
388
389
                // merge xml nodes
390
                $nodeToBeAppended = $doc1->importNode($domNode2, true);
391
                $domNode->item(0)->appendChild($nodeToBeAppended);
392
393
                // add to modsData (merge not required)
394
                // get mods tag
395
                foreach ($this->xmlData->childNodes as $childNode) {
396
                    // Skip comments inside the xml.
397
                    if ($childNode instanceof \DOMElement) {
398
                        $firstChild = $childNode;
399
                        break;
400
                    }
401
                }
402
                //$firstChild = $this->xmlData->childNodes->item(0);
403
                $firstItem = $doc1->documentElement->firstChild;
404
                $nodeAppendModsData = $this->xmlData->importNode($firstItem, true);
405
                $firstChild->appendChild($nodeAppendModsData);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $firstChild does not seem to be defined for all execution paths leading up to this point.
Loading history...
406
407
                return $doc1->saveXML();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $doc1->saveXML() returns the type string which is incompatible with the documented return type EWW\Dpf\Services\xml.
Loading history...
408
            }
409
        } else {
410
            // attribute only
411
            $xml = $this->parseXPath($xPath);
412
413
            // FIXME: XPATHXmlGenerator XPATH does not generate any namespaces,
414
            // which DOMDocument cannot cope with. Actually, namespaces should not be necessary here,
415
            // since it is about child elements that are then added to the overall XML.
416
            libxml_use_internal_errors(true);
417
            $docXML = new \DOMDocument();
418
            $docXML->loadXML($xml);
419
            libxml_use_internal_errors(false);
420
421
            $domXPath = \EWW\Dpf\Helper\XPath::create($this->xmlData);
422
            $domNode  = $domXPath->query('/data');
423
424
            $node = $docXML->documentElement;
425
426
            $nodeAppendModsData = $this->xmlData->importNode($node, true);
427
            $domNode->item($domNode->length - 1)->appendChild($nodeAppendModsData);
428
429
            return $docXML->saveXML();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $docXML->saveXML() returns the type string which is incompatible with the documented return type EWW\Dpf\Services\xml.
Loading history...
430
        }
431
432
        return $this->xmlData->saveXML();
433
    }
434
435
    public function setXML($value = '') {
436
        $domDocument = new \DOMDocument();
437
        if (is_null(@$domDocument->loadXML($value))) {
438
            throw new \Exception("Couldn't load MODS data");
439
        }
440
        $this->xmlData = $domDocument;
0 ignored issues
show
Documentation Bug introduced by
It seems like $domDocument of type DOMDocument is incompatible with the declared type EWW\Dpf\Services\DOMDocument of property $xmlData.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
441
    }
442
}
443