Completed
Push — master ( 03866a...251c0b )
by Adrien
03:24
created

XMLReader::convertURIToUseRealPath()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 16
rs 9.4285
ccs 11
cts 11
cp 1
cc 3
eloc 10
nc 3
nop 1
crap 3

2 Methods

Rating   Name   Duplication   Size   Complexity  
A XMLReader::getRealPathURIForFileInZip() 0 4 1
A XMLReader::isRunningHHVM() 0 4 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 231
    public function openFileInZip($zipFilePath, $fileInsideZipPath)
27
    {
28 231
        $wasOpenSuccessful = false;
29 231
        $realPathURI = $this->getRealPathURIForFileInZip($zipFilePath, $fileInsideZipPath);
30
31
        // HHVM does not check if file exists within zip file
32
        // @link https://github.com/facebook/hhvm/issues/5779
33 231
        if ($this->isRunningHHVM()) {
34
            if ($this->fileExistsWithinZip($realPathURI)) {
35
                $wasOpenSuccessful = $this->open($realPathURI, null, LIBXML_NONET);
36
            }
37
        } else {
38 231
            $wasOpenSuccessful = $this->open($realPathURI, null, LIBXML_NONET);
39
        }
40
41 231
        return $wasOpenSuccessful;
42
    }
43
44
    /**
45
     * Returns the real path for the given path components.
46
     * This is useful to avoid issues on some Windows setup.
47
     *
48
     * @param string $zipFilePath Path to the ZIP file
49
     * @param string $fileInsideZipPath Relative or absolute path of the file inside the zip
50
     * @return string The real path URI
51
     */
52 237
    public function getRealPathURIForFileInZip($zipFilePath, $fileInsideZipPath)
53
    {
54 237
        return (self::ZIP_WRAPPER . realpath($zipFilePath) . '#' . $fileInsideZipPath);
55
    }
56
57
    /**
58
     * Returns whether the current environment is HHVM
59
     *
60
     * @return bool TRUE if running on HHVM, FALSE otherwise
61
     */
62 231
    protected function isRunningHHVM()
63
    {
64 231
        return defined('HHVM_VERSION');
65
    }
66
67
    /**
68
     * Returns whether the file at the given location exists
69
     *
70
     * @param string $zipStreamURI URI of a zip stream, e.g. "zip://file.zip#path/inside.xml"
71
     * @return bool TRUE if the file exists, FALSE otherwise
72
     */
73 15
    protected function fileExistsWithinZip($zipStreamURI)
74
    {
75 15
        $doesFileExists = false;
76
77 15
        $pattern = '/zip:\/\/([^#]+)#(.*)/';
78 15
        if (preg_match($pattern, $zipStreamURI, $matches)) {
79 15
            $zipFilePath = $matches[1];
80 15
            $innerFilePath = $matches[2];
81
82 15
            $zip = new \ZipArchive();
83 15
            if ($zip->open($zipFilePath) === true) {
84 15
                $doesFileExists = ($zip->locateName($innerFilePath) !== false);
85 15
                $zip->close();
86 15
            }
87 15
        }
88
89 15
        return $doesFileExists;
90
    }
91
92
    /**
93
     * Move to next node in document
94
     * @see \XMLReader::read
95
     *
96
     * @return bool TRUE on success or FALSE on failure
97
     * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred
98
     */
99 246
    public function read()
100
    {
101 246
        $this->useXMLInternalErrors();
102
103 246
        $wasReadSuccessful = parent::read();
104
105 246
        $this->resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured();
106
107 246
        return $wasReadSuccessful;
108
    }
109
110
    /**
111
     * Read until the element with the given name is found, or the end of the file.
112
     *
113
     * @param string $nodeName Name of the node to find
114
     * @return bool TRUE on success or FALSE on failure
115
     * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred
116
     */
117 201
    public function readUntilNodeFound($nodeName)
118
    {
119
        do {
120 201
            $wasReadSuccessful = $this->read();
121 201
            $isNotPositionedOnStartingNode = !$this->isPositionedOnStartingNode($nodeName);
122 201
        } while ($wasReadSuccessful && $isNotPositionedOnStartingNode);
123
124 198
        return $wasReadSuccessful;
125
    }
126
127
    /**
128
     * Move cursor to next node skipping all subtrees
129
     * @see \XMLReader::next
130
     *
131
     * @param string|void $localName The name of the next node to move to
132
     * @return bool TRUE on success or FALSE on failure
133
     * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred
134
     */
135 93
    public function next($localName = null)
136
    {
137 93
        $this->useXMLInternalErrors();
138
139 93
        $wasNextSuccessful = parent::next($localName);
140
141 93
        $this->resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured();
142
143 87
        return $wasNextSuccessful;
144
    }
145
146
    /**
147
     * @param string $nodeName
148
     * @return bool Whether the XML Reader is currently positioned on the starting node with given name
149
     */
150 243
    public function isPositionedOnStartingNode($nodeName)
151
    {
152 243
        return $this->isPositionedOnNode($nodeName, XMLReader::ELEMENT);
153
    }
154
155
    /**
156
     * @param string $nodeName
157
     * @return bool Whether the XML Reader is currently positioned on the ending node with given name
158
     */
159 159
    public function isPositionedOnEndingNode($nodeName)
160
    {
161 159
        return $this->isPositionedOnNode($nodeName, XMLReader::END_ELEMENT);
162
    }
163
164
    /**
165
     * @param string $nodeName
166
     * @param int $nodeType
167
     * @return bool Whether the XML Reader is currently positioned on the node with given name and type
168
     */
169 243
    private function isPositionedOnNode($nodeName, $nodeType)
170
    {
171
        // In some cases, the node has a prefix (for instance, "<sheet>" can also be "<x:sheet>").
172
        // So if the given node name does not have a prefix, we need to look at the unprefixed name ("localName").
173
        // @see https://github.com/box/spout/issues/233
174 243
        $hasPrefix = (strpos($nodeName, ':') !== false);
175 243
        $currentNodeName = ($hasPrefix) ? $this->name : $this->localName;
176
177 243
        return ($this->nodeType === $nodeType && $currentNodeName === $nodeName);
178
    }
179
}
180