Completed
Push — master ( 4a6546...7f8b95 )
by Adrien
02:30
created

XMLReader::isRunningHHVM()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Box\Spout\Reader\Wrapper;
4
5
6
/**
7
 * Class XMLReader
8
 * Wrapper around the built-in XMLReader
9
 * @see \XMLReader
10
 *
11
 * @package Box\Spout\Reader\Wrapper
12
 */
13
class XMLReader extends \XMLReader
14
{
15
    use XMLInternalErrorsHelper;
16
17
    const ZIP_WRAPPER = 'zip://';
18
19
    /**
20
     * Opens the XML Reader to read a file located inside a ZIP file.
21
     *
22
     * @param string $zipFilePath Path to the ZIP file
23
     * @param string $fileInsideZipPath Relative or absolute path of the file inside the zip
24
     * @return bool TRUE on success or FALSE on failure
25
     */
26 291
    public function openFileInZip($zipFilePath, $fileInsideZipPath)
27
    {
28 291
        $wasOpenSuccessful = false;
29 291
        $realPathURI = $this->getRealPathURIForFileInZip($zipFilePath, $fileInsideZipPath);
30
31
        // We need to check first that the file we are trying to read really exist because:
32
        //  - PHP emits a warning when trying to open a file that does not exist.
33
        //  - HHVM does not check if file exists within zip file (@link https://github.com/facebook/hhvm/issues/5779)
34 291
        if ($this->fileExistsWithinZip($realPathURI)) {
35 285
            $wasOpenSuccessful = $this->open($realPathURI, null, LIBXML_NONET);
36 285
        }
37
38 291
        return $wasOpenSuccessful;
39
    }
40
41
    /**
42
     * Returns the real path for the given path components.
43
     * This is useful to avoid issues on some Windows setup.
44
     *
45
     * @param string $zipFilePath Path to the ZIP file
46
     * @param string $fileInsideZipPath Relative or absolute path of the file inside the zip
47
     * @return string The real path URI
48
     */
49 297
    public function getRealPathURIForFileInZip($zipFilePath, $fileInsideZipPath)
50
    {
51 297
        return (self::ZIP_WRAPPER . realpath($zipFilePath) . '#' . $fileInsideZipPath);
52
    }
53
54
    /**
55
     * Returns whether the file at the given location exists
56
     *
57
     * @param string $zipStreamURI URI of a zip stream, e.g. "zip://file.zip#path/inside.xml"
58
     * @return bool TRUE if the file exists, FALSE otherwise
59
     */
60 306
    protected function fileExistsWithinZip($zipStreamURI)
61
    {
62 306
        $doesFileExists = false;
63
64 306
        $pattern = '/zip:\/\/([^#]+)#(.*)/';
65 306
        if (preg_match($pattern, $zipStreamURI, $matches)) {
66 306
            $zipFilePath = $matches[1];
67 306
            $innerFilePath = $matches[2];
68
69 306
            $zip = new \ZipArchive();
70 306
            if ($zip->open($zipFilePath) === true) {
71 306
                $doesFileExists = ($zip->locateName($innerFilePath) !== false);
72 306
                $zip->close();
73 306
            }
74 306
        }
75
76 306
        return $doesFileExists;
77
    }
78
79
    /**
80
     * Move to next node in document
81
     * @see \XMLReader::read
82
     *
83
     * @return bool TRUE on success or FALSE on failure
84
     * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred
85
     */
86 306
    public function read()
87
    {
88 306
        $this->useXMLInternalErrors();
89
90 306
        $wasReadSuccessful = parent::read();
91
92 306
        $this->resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured();
93
94 306
        return $wasReadSuccessful;
95
    }
96
97
    /**
98
     * Read until the element with the given name is found, or the end of the file.
99
     *
100
     * @param string $nodeName Name of the node to find
101
     * @return bool TRUE on success or FALSE on failure
102
     * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred
103
     */
104 249
    public function readUntilNodeFound($nodeName)
105
    {
106
        do {
107 249
            $wasReadSuccessful = $this->read();
108 249
            $isNotPositionedOnStartingNode = !$this->isPositionedOnStartingNode($nodeName);
109 249
        } while ($wasReadSuccessful && $isNotPositionedOnStartingNode);
110
111 246
        return $wasReadSuccessful;
112
    }
113
114
    /**
115
     * Move cursor to next node skipping all subtrees
116
     * @see \XMLReader::next
117
     *
118
     * @param string|void $localName The name of the next node to move to
119
     * @return bool TRUE on success or FALSE on failure
120
     * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred
121
     */
122 105
    public function next($localName = null)
123
    {
124 105
        $this->useXMLInternalErrors();
125
126 105
        $wasNextSuccessful = parent::next($localName);
127
128 105
        $this->resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured();
129
130 99
        return $wasNextSuccessful;
131
    }
132
133
    /**
134
     * @param string $nodeName
135
     * @return bool Whether the XML Reader is currently positioned on the starting node with given name
136
     */
137 303
    public function isPositionedOnStartingNode($nodeName)
138
    {
139 303
        return $this->isPositionedOnNode($nodeName, XMLReader::ELEMENT);
140
    }
141
142
    /**
143
     * @param string $nodeName
144
     * @return bool Whether the XML Reader is currently positioned on the ending node with given name
145
     */
146 105
    public function isPositionedOnEndingNode($nodeName)
147
    {
148 105
        return $this->isPositionedOnNode($nodeName, XMLReader::END_ELEMENT);
149
    }
150
151
    /**
152
     * @param string $nodeName
153
     * @param int $nodeType
154
     * @return bool Whether the XML Reader is currently positioned on the node with given name and type
155
     */
156 303
    private function isPositionedOnNode($nodeName, $nodeType)
157
    {
158
        // In some cases, the node has a prefix (for instance, "<sheet>" can also be "<x:sheet>").
159
        // So if the given node name does not have a prefix, we need to look at the unprefixed name ("localName").
160
        // @see https://github.com/box/spout/issues/233
161 303
        $hasPrefix = (strpos($nodeName, ':') !== false);
162 303
        $currentNodeName = ($hasPrefix) ? $this->name : $this->localName;
163
164 303
        return ($this->nodeType === $nodeType && $currentNodeName === $nodeName);
165
    }
166
}
167