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