Differences Between: [Versions 310 and 311] [Versions 311 and 401] [Versions 39 and 311]
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 public function read() 87 { 88 $this->useXMLInternalErrors(); 89 90 $wasReadSuccessful = parent::read(); 91 92 $this->resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured(); 93 94 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 * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred 102 * @return bool TRUE on success or FALSE on failure 103 */ 104 public function readUntilNodeFound($nodeName) 105 { 106 do { 107 $wasReadSuccessful = $this->read(); 108 $isNotPositionedOnStartingNode = !$this->isPositionedOnStartingNode($nodeName); 109 } while ($wasReadSuccessful && $isNotPositionedOnStartingNode); 110 111 return $wasReadSuccessful; 112 } 113 114 /** 115 * Move cursor to next node skipping all subtrees 116 * @see \XMLReader::next 117 * 118 * @param string|null $localName The name of the next node to move to 119 * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred 120 * @return bool TRUE on success or FALSE on failure 121 */ 122 public function next($localName = null) 123 { 124 $this->useXMLInternalErrors(); 125 126 $wasNextSuccessful = parent::next($localName); 127 128 $this->resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured(); 129 130 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 public function isPositionedOnStartingNode($nodeName) 138 { 139 return $this->isPositionedOnNode($nodeName, self::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 public function isPositionedOnEndingNode($nodeName) 147 { 148 return $this->isPositionedOnNode($nodeName, self::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 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 $hasPrefix = (\strpos($nodeName, ':') !== false); 162 $currentNodeName = ($hasPrefix) ? $this->name : $this->localName; 163 164 return ($this->nodeType === $nodeType && $currentNodeName === $nodeName); 165 } 166 167 /** 168 * @return string The name of the current node, un-prefixed 169 */ 170 public function getCurrentNodeName() 171 { 172 return $this->localName; 173 } 174 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body