Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?phpdeclare(strict_types=1);namespace OpenSpout\Reader\Wrapper;use OpenSpout\Reader\Exception\XMLProcessingException;use ZipArchive;/*** @internal*/final class XMLReader extends \XMLReader{use XMLInternalErrorsHelper;public const ZIP_WRAPPER = 'zip://';/*** Opens the XML Reader to read a file located inside a ZIP file.** @param string $zipFilePath Path to the ZIP file* @param string $fileInsideZipPath Relative or absolute path of the file inside the zip** @return bool TRUE on success or FALSE on failure*/public function openFileInZip(string $zipFilePath, string $fileInsideZipPath): bool{$wasOpenSuccessful = false;$realPathURI = $this->getRealPathURIForFileInZip($zipFilePath, $fileInsideZipPath);// We need to check first that the file we are trying to read really exist because:// - PHP emits a warning when trying to open a file that does not exist.if ($this->fileExistsWithinZip($realPathURI)) {$wasOpenSuccessful = $this->open($realPathURI, null, LIBXML_NONET);}return $wasOpenSuccessful;}/*** Returns the real path for the given path components.* This is useful to avoid issues on some Windows setup.** @param string $zipFilePath Path to the ZIP file* @param string $fileInsideZipPath Relative or absolute path of the file inside the zip** @return string The real path URI*/public function getRealPathURIForFileInZip(string $zipFilePath, string $fileInsideZipPath): string{// The file path should not start with a '/', otherwise it won't be found$fileInsideZipPathWithoutLeadingSlash = ltrim($fileInsideZipPath, '/');return self::ZIP_WRAPPER.realpath($zipFilePath).'#'.$fileInsideZipPathWithoutLeadingSlash;}/*** Move to next node in document.** @see \XMLReader::read** @throws XMLProcessingException If an error/warning occurred*/public function read(): bool{$this->useXMLInternalErrors();$wasReadSuccessful = parent::read();$this->resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured();return $wasReadSuccessful;}/*** Read until the element with the given name is found, or the end of the file.** @param string $nodeName Name of the node to find** @return bool TRUE on success or FALSE on failure** @throws XMLProcessingException If an error/warning occurred*/public function readUntilNodeFound(string $nodeName): bool{do {$wasReadSuccessful = $this->read();$isNotPositionedOnStartingNode = !$this->isPositionedOnStartingNode($nodeName);} while ($wasReadSuccessful && $isNotPositionedOnStartingNode);return $wasReadSuccessful;}/*** Move cursor to next node skipping all subtrees.** @see \XMLReader::next** @param null|string $localName The name of the next node to move to** @throws XMLProcessingException If an error/warning occurred*/public function next($localName = null): bool{$this->useXMLInternalErrors();$wasNextSuccessful = parent::next($localName);$this->resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured();return $wasNextSuccessful;}/*** @return bool Whether the XML Reader is currently positioned on the starting node with given name*/public function isPositionedOnStartingNode(string $nodeName): bool{return $this->isPositionedOnNode($nodeName, self::ELEMENT);}/*** @return bool Whether the XML Reader is currently positioned on the ending node with given name*/public function isPositionedOnEndingNode(string $nodeName): bool{return $this->isPositionedOnNode($nodeName, self::END_ELEMENT);}/*** @return string The name of the current node, un-prefixed*/public function getCurrentNodeName(): string{return $this->localName;}/*** Returns whether the file at the given location exists.** @param string $zipStreamURI URI of a zip stream, e.g. "zip://file.zip#path/inside.xml"** @return bool TRUE if the file exists, FALSE otherwise*/private function fileExistsWithinZip(string $zipStreamURI): bool{$doesFileExists = false;$pattern = '/zip:\/\/([^#]+)#(.*)/';if (1 === preg_match($pattern, $zipStreamURI, $matches)) {$zipFilePath = $matches[1];$innerFilePath = $matches[2];$zip = new ZipArchive();if (true === $zip->open($zipFilePath)) {$doesFileExists = (false !== $zip->locateName($innerFilePath));$zip->close();}}return $doesFileExists;}/*** @return bool Whether the XML Reader is currently positioned on the node with given name and type*/private function isPositionedOnNode(string $nodeName, int $nodeType): bool{/*** In some cases, the node has a prefix (for instance, "<sheet>" can also be "<x:sheet>").* So if the given node name does not have a prefix, we need to look at the unprefixed name ("localName").** @see https://github.com/box/spout/issues/233*/$hasPrefix = str_contains($nodeName, ':');$currentNodeName = ($hasPrefix) ? $this->name : $this->localName;return $this->nodeType === $nodeType && $currentNodeName === $nodeName;}}