Passed
Push — master ( 548996...0a3947 )
by
unknown
14:39
created

ExtensionXmlPushParser::parseXml()   B

Complexity

Conditions 7
Paths 13

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
c 0
b 0
f 0
dl 0
loc 29
rs 8.8333
cc 7
nc 13
nop 1
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
17
18
use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
19
20
/**
21
 * Parser for TYPO3's extension.xml file.
22
 *
23
 * Depends on PHP ext/xml which should be available
24
 * with PHP 4+. This is the parser used in TYPO3
25
 * Core <= 4.3 (without the "collect all data in one
26
 * array" behaviour).
27
 * Notice: ext/xml has proven to be buggy with entities.
28
 * Use at least PHP 5.2.9+ and libxml2 2.7.3+!
29
 * @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
30
 */
31
class ExtensionXmlPushParser extends AbstractExtensionXmlParser
32
{
33
    /**
34
     * Keeps current data of element to process.
35
     *
36
     * @var string
37
     */
38
    protected $elementData = '';
39
40
    /**
41
     * Class constructor.
42
     */
43
    public function __construct()
44
    {
45
        $this->requiredPhpExtensions = 'xml';
46
    }
47
48
    /**
49
     * Create required parser
50
     */
51
    protected function createParser()
52
    {
53
        $this->objXml = xml_parser_create();
54
        xml_set_object($this->objXml, $this);
55
    }
56
57
    /**
58
     * Method parses an extensions.xml file.
59
     *
60
     * @param string $file GZIP stream resource
61
     * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of parse errors
62
     */
63
    public function parseXml($file)
64
    {
65
        $this->createParser();
66
        if (!is_resource($this->objXml)) {
67
            throw new ExtensionManagerException('Unable to create XML parser.', 1342640663);
68
        }
69
        // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
70
        $previousValueOfEntityLoader = null;
71
        if (PHP_MAJOR_VERSION < 8) {
72
            $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
73
        }
74
        // keep original character case of XML document
75
        xml_parser_set_option($this->objXml, XML_OPTION_CASE_FOLDING, false);
76
        xml_parser_set_option($this->objXml, XML_OPTION_SKIP_WHITE, false);
77
        xml_parser_set_option($this->objXml, XML_OPTION_TARGET_ENCODING, 'utf-8');
78
        xml_set_element_handler($this->objXml, [$this, 'startElement'], [$this, 'endElement']);
79
        xml_set_character_data_handler($this->objXml, [$this, 'characterData']);
80
        if (!($fp = fopen($file, 'r'))) {
81
            throw new ExtensionManagerException(sprintf('Unable to open file resource %s.', $file), 1342640689);
82
        }
83
        while ($data = fread($fp, 4096)) {
84
            if (!xml_parse($this->objXml, $data, feof($fp))) {
85
                throw new ExtensionManagerException(sprintf('XML error %s in line %u of file resource %s.', xml_error_string(xml_get_error_code($this->objXml)), xml_get_current_line_number($this->objXml), $file), 1342640703);
86
            }
87
        }
88
        if (PHP_MAJOR_VERSION < 8) {
89
            libxml_disable_entity_loader($previousValueOfEntityLoader);
90
        }
91
        xml_parser_free($this->objXml);
92
    }
93
94
    /**
95
     * Method is invoked when parser accesses start tag of an element.
96
     *
97
     * @param resource $parser parser resource
98
     * @param string $elementName element name at parser's current position
99
     * @param array $attrs array of an element's attributes if available
100
     */
101
    protected function startElement($parser, $elementName, $attrs)
102
    {
103
        switch ($elementName) {
104
            case 'extension':
105
                $this->extensionKey = $attrs['extensionkey'];
106
                break;
107
            case 'version':
108
                $this->version = $attrs['version'];
109
                break;
110
            default:
111
                $this->elementData = '';
112
        }
113
    }
114
115
    /**
116
     * Method is invoked when parser accesses end tag of an element.
117
     *
118
     * @param resource $parser parser resource
119
     * @param string $elementName Element name at parser's current position
120
     */
121
    protected function endElement($parser, $elementName)
122
    {
123
        switch ($elementName) {
124
            case 'extension':
125
                $this->resetProperties(true);
126
                break;
127
            case 'version':
128
                $this->notify();
129
                $this->resetProperties();
130
                break;
131
            case 'downloadcounter':
132
                // downloadcounter could be a child node of
133
                // extension or version
134
                if ($this->version == null) {
135
                    $this->extensionDownloadCounter = $this->elementData;
136
                } else {
137
                    $this->versionDownloadCounter = $this->elementData;
138
                }
139
                break;
140
            case 'title':
141
                $this->title = $this->elementData;
142
                break;
143
            case 'description':
144
                $this->description = $this->elementData;
145
                break;
146
            case 'state':
147
                $this->state = $this->elementData;
148
                break;
149
            case 'reviewstate':
150
                $this->reviewstate = $this->elementData;
151
                break;
152
            case 'category':
153
                $this->category = $this->elementData;
154
                break;
155
            case 'lastuploaddate':
156
                $this->lastuploaddate = $this->elementData;
157
                break;
158
            case 'uploadcomment':
159
                $this->uploadcomment = $this->elementData;
160
                break;
161
            case 'dependencies':
162
                $this->dependencies = $this->convertDependencies($this->elementData);
163
                break;
164
            case 'authorname':
165
                $this->authorname = $this->elementData;
166
                break;
167
            case 'authoremail':
168
                $this->authoremail = $this->elementData;
169
                break;
170
            case 'authorcompany':
171
                $this->authorcompany = $this->elementData;
172
                break;
173
            case 'ownerusername':
174
                $this->ownerusername = $this->elementData;
175
                break;
176
            case 't3xfilemd5':
177
                $this->t3xfilemd5 = $this->elementData;
178
                break;
179
            case 'documentation_link':
180
                $this->documentationLink = $this->elementData;
181
                break;
182
        }
183
    }
184
185
    /**
186
     * Method is invoked when parser accesses any character other than elements.
187
     *
188
     * @param resource $parser parser resource
189
     * @param string $data An element's value
190
     */
191
    protected function characterData($parser, $data)
192
    {
193
        $this->elementData .= $data;
194
    }
195
}
196