Passed
Pull Request — master (#207)
by
unknown
12:49
created

ParserGenerator   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 419
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 39
eloc 178
c 0
b 0
f 0
dl 0
loc 419
rs 9.28

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 29 4
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
     *
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
     * ParserGenerator constructor.
94
     * @param int $clientPid
95
     */
96
    public function __construct($clientPid = 0)
97
    {
98
        $objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class);
99
        $this->clientConfigurationManager = $objectManager->get(ClientConfigurationManager::class);
100
101
        if ($clientPid) {
102
            $this->clientConfigurationManager->setConfigurationPid($clientPid);
103
        }
104
105
        $this->documentTypeRepository = $objectManager->get(DocumentTypeRepository::class);
106
107
        $namespaceConfigurationString = $this->clientConfigurationManager->getNamespaces();
108
        if (!empty($namespaceConfigurationString)) {
109
                        $namespaceConfiguration = explode(";", $namespaceConfigurationString);
110
                        foreach ($namespaceConfiguration as $value) {
111
                                $namespace = explode("=", $value);
112
                                $this->namespaceString .= ' xmlns:' . $namespace[0] . '="' . $namespace[1] . '"';
113
                            }
114
         }
115
116
        $this->xmlHeader = '<data' . $this->namespaceString . '></data>';
117
118
        $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...
119
        $this->xmlData->loadXML($this->xmlHeader);
120
121
        // Parser
122
        include_once 'XPathXMLGenerator.php';
123
124
        $this->parser = new XPathXMLGenerator();
125
    }
126
127
    /**
128
     * returns the mods xml string
129
     * @return string mods xml
130
     */
131
    public function getXMLData()
132
    {
133
        $xml = $this->xmlData->saveXML();
134
        $xml = preg_replace("/eww=\"\d-\d-\d\"/", '${1}${2}${3}', $xml);
135
136
        return $xml;
137
    }
138
139
    /**
140
     * build mods from form array
141
     * @param array $array structured form data array
142
     */
143
    public function buildXmlFromForm($array)
144
    {
145
        $fedoraNamespace = $this->clientConfigurationManager->getFedoraNamespace();
146
147
        $this->xmlData = $this->xmlData;
148
        // Build xml mods from form fields
149
        // loop each group
150
        foreach ($array['metadata'] as $key => $group) {
151
            //groups
152
            $mapping = $group['mapping'];
153
154
            $values     = $group['values'];
155
            $attributes = $group['attributes'];
156
157
            $attributeXPath     = '';
158
            $extensionAttribute = '';
159
            foreach ($attributes as $attribute) {
160
                if (!$attribute["modsExtension"]) {
161
                    $attributeXPath .= '[' . $attribute['mapping'] . '="' . $attribute['value'] . '"]';
162
                } else {
163
                    $extensionAttribute .= '[' . $attribute['mapping'] . '="' . $attribute['value'] . '"]';
164
                }
165
166
            }
167
168
            // mods extension
169
            if ($group['modsExtensionMapping']) {
170
                $counter = sprintf("%'03d", $this->counter);
171
                $attributeXPath .= '[@ID="'.$fedoraNamespace.'_' . $counter . '"]';
172
            }
173
174
            $existsExtensionFlag = false;
175
            $i                   = 0;
176
            // loop each object
177
            if (!empty($values)) {
178
                //$values = empty($values)? [] : $values;
179
                foreach ($values as $value) {
180
181
                    if ($value['modsExtension']) {
182
                        $existsExtensionFlag = true;
183
                        // mods extension
184
                        $counter = sprintf("%'03d", $this->counter);
185
                        $referenceAttribute = $extensionAttribute . '[@' . $group['modsExtensionReference'] . '="' . $fedoraNamespace . '_' . $counter . '"]';
186
187
                        $path = $group['modsExtensionMapping'] . $referenceAttribute . '%/' . $value['mapping'];
188
189
                        $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

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

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