Completed
Pull Request — develop (#35)
by
unknown
04:12
created

XMLReader::libxmlDisable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * This file is part of PHPOffice Common
4
 *
5
 * PHPOffice Common is free software distributed under the terms of the GNU Lesser
6
 * General Public License version 3 as published by the Free Software Foundation.
7
 *
8
 * For the full copyright and license information, please read the LICENSE
9
 * file that was distributed with this source code. For the full list of
10
 * contributors, visit https://github.com/PHPOffice/Common/contributors.
11
 *
12
 * @link        https://github.com/PHPOffice/Common
13
 * @copyright   2009-2016 PHPOffice Common contributors
14
 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
15
 */
16
17
namespace PhpOffice\Common;
18
19
/**
20
 * XML Reader wrapper
21
 *
22
 * @since   0.2.1
23
 */
24
class XMLReader
25
{
26
    /**
27
     * DOMDocument object
28
     *
29
     * @var \DOMDocument
30
     */
31
    private $dom = null;
32
33
    /**
34
     * DOMXpath object
35
     *
36
     * @var \DOMXpath
37
     */
38
    private $xpath = null;
39
40
    /**
41
     * Get DOMDocument from ZipArchive
42
     *
43
     * @param string $zipFile
44
     * @param string $xmlFile
45
     * @return \DOMDocument|false
46
     * @throws \Exception
47
     */
48 2
    public function getDomFromZip($zipFile, $xmlFile)
49
    {
50 2
        if (file_exists($zipFile) === false) {
51 1
            throw new \Exception('Cannot find archive file.');
52
        }
53
54 1
        $zip = new \ZipArchive();
55 1
        $zip->open($zipFile);
56 1
        $content = $zip->getFromName($xmlFile);
57 1
        $zip->close();
58
59 1
        if ($content === false) {
60 1
            return false;
61
        }
62
63 1
        return $this->getDomFromString($content);
64
    }
65
66
    /**
67
     * Call libxml_disable_entity_loader, but only for PHP < 8
68
     *
69
     * @param bool $value
70
     * @return bool
71
     */
72 6
    public static function libxmlDisable($value)
73
    {
74 6
        return version_compare(PHP_VERSION, '8') < 0 && libxml_disable_entity_loader($value);
75
    }
76
77
    /**
78
     * Get DOMDocument from content string
79
     *
80
     * @param string $content
81
     * @return \DOMDocument
82
     */
83 6
    public function getDomFromString($content)
84
    {
85 6
        $originalLibXMLEntityValue = self::libxmlDisable(true);
86 6
        $this->dom = new \DOMDocument();
87 6
        $this->dom->loadXML($content);
88 6
        self::libxmlDisable($originalLibXMLEntityValue);
89
90 6
        return $this->dom;
91
    }
92
93
    /**
94
     * Get elements
95
     *
96
     * @param string $path
97
     * @param \DOMElement $contextNode
98
     * @return \DOMNodeList
99
     */
100 6
    public function getElements($path, \DOMElement $contextNode = null)
101
    {
102 6
        if ($this->dom === null) {
103 1
            return array();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type documented by PhpOffice\Common\XMLReader::getElements of type DOMNodeList.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
104
        }
105 6
        if ($this->xpath === null) {
106 5
            $this->xpath = new \DOMXpath($this->dom);
107
        }
108
109 6
        if (is_null($contextNode)) {
110 6
            return $this->xpath->query($path);
111
        }
112
113 1
        return $this->xpath->query($path, $contextNode);
114
    }
115
116
    /**
117
     * Registers the namespace with the DOMXPath object
118
     *
119
     * @param string $prefix The prefix
120
     * @param string $namespaceURI The URI of the namespace
121
     * @return bool true on success or false on failure
122
     * @throws \InvalidArgumentException If called before having loaded the DOM document
123
     */
124 2
    public function registerNamespace($prefix, $namespaceURI)
125
    {
126 2
        if ($this->dom === null) {
127 1
            throw new \InvalidArgumentException('Dom needs to be loaded before registering a namespace');
128
        }
129 1
        if ($this->xpath === null) {
130 1
            $this->xpath = new \DOMXpath($this->dom);
131
        }
132 1
        return $this->xpath->registerNamespace($prefix, $namespaceURI);
133
    }
134
135
    /**
136
     * Get element
137
     *
138
     * @param string $path
139
     * @param \DOMElement $contextNode
140
     * @return \DOMElement|null
141
     */
142 3
    public function getElement($path, \DOMElement $contextNode = null)
143
    {
144 3
        $elements = $this->getElements($path, $contextNode);
145 3
        if ($elements->length > 0) {
146 2
            return $elements->item(0);
147
        }
148
149 1
        return null;
150
    }
151
152
    /**
153
     * Get element attribute
154
     *
155
     * @param string $attribute
156
     * @param \DOMElement $contextNode
157
     * @param string $path
158
     * @return string|null
159
     */
160 1
    public function getAttribute($attribute, \DOMElement $contextNode = null, $path = null)
161
    {
162 1
        $return = null;
163 1
        if ($path !== null) {
164 1
            $elements = $this->getElements($path, $contextNode);
165 1
            if ($elements->length > 0) {
166
                /** @var \DOMElement $node Type hint */
167 1
                $node = $elements->item(0);
168 1
                $return = $node->getAttribute($attribute);
169
            }
170
        } else {
171 1
            if ($contextNode !== null) {
172 1
                $return = $contextNode->getAttribute($attribute);
173
            }
174
        }
175
176 1
        return ($return == '') ? null : $return;
177
    }
178
179
    /**
180
     * Get element value
181
     *
182
     * @param string $path
183
     * @param \DOMElement $contextNode
184
     * @return string|null
185
     */
186 2
    public function getValue($path, \DOMElement $contextNode = null)
187
    {
188 2
        $elements = $this->getElements($path, $contextNode);
189 2
        if ($elements->length > 0) {
190 1
            return $elements->item(0)->nodeValue;
191
        }
192
193 1
        return null;
194
    }
195
196
    /**
197
     * Count elements
198
     *
199
     * @param string $path
200
     * @param \DOMElement $contextNode
201
     * @return integer
202
     */
203 1
    public function countElements($path, \DOMElement $contextNode = null)
204
    {
205 1
        $elements = $this->getElements($path, $contextNode);
206
207 1
        return $elements->length;
208
    }
209
210
    /**
211
     * Element exists
212
     *
213
     * @param string $path
214
     * @param \DOMElement $contextNode
215
     * @return boolean
216
     */
217 4
    public function elementExists($path, \DOMElement $contextNode = null)
218
    {
219 4
        return $this->getElements($path, $contextNode)->length > 0;
220
    }
221
}
222