diff --git a/src/Spout/Common/Helper/GlobalFunctionsHelper.php b/src/Spout/Common/Helper/GlobalFunctionsHelper.php index 6b5d7e50..8243bcc0 100644 --- a/src/Spout/Common/Helper/GlobalFunctionsHelper.php +++ b/src/Spout/Common/Helper/GlobalFunctionsHelper.php @@ -164,7 +164,43 @@ public function file_exists($fileName) */ public function file_get_contents($filePath) { - return file_get_contents($filePath); + $realFilePath = $this->convertToUseRealPath($filePath); + return file_get_contents($realFilePath); + } + + /** + * Updates the given file path to use a real path. + * This is to avoid issues on some Windows setup. + * + * @param string $filePath File path + * @return string The file path using a real path + */ + protected function convertToUseRealPath($filePath) + { + $realFilePath = $filePath; + + if ($this->isZipStream($filePath)) { + if (preg_match('/zip:\/\/(.*)#(.*)/', $filePath, $matches)) { + $documentPath = $matches[1]; + $documentInsideZipPath = $matches[2]; + $realFilePath = 'zip://' . realpath($documentPath) . '#' . $documentInsideZipPath; + } + } else { + $realFilePath = realpath($filePath); + } + + return $realFilePath; + } + + /** + * Returns whether the given path is a zip stream. + * + * @param string $path Path pointing to a document + * @return bool TRUE if path is a zip stream, FALSE otherwise + */ + protected function isZipStream($path) + { + return (strpos($path, 'zip://') === 0); } /** diff --git a/src/Spout/Reader/AbstractReader.php b/src/Spout/Reader/AbstractReader.php index ece60cab..ee6af08f 100644 --- a/src/Spout/Reader/AbstractReader.php +++ b/src/Spout/Reader/AbstractReader.php @@ -72,7 +72,9 @@ public function open($filePath) } try { - $this->openReader($filePath); + // Need to use realpath to fix "Can't open file" on some Windows setup + $fileRealPath = realpath($filePath); + $this->openReader($fileRealPath); $this->isStreamOpened = true; } catch (\Exception $exception) { throw new IOException("Could not open $filePath for reading! ({$exception->getMessage()})"); diff --git a/src/Spout/Reader/Wrapper/XMLReader.php b/src/Spout/Reader/Wrapper/XMLReader.php index d48013fe..42bd92c2 100644 --- a/src/Spout/Reader/Wrapper/XMLReader.php +++ b/src/Spout/Reader/Wrapper/XMLReader.php @@ -26,20 +26,45 @@ class XMLReader extends \XMLReader public function open($URI, $encoding = null, $options = 0) { $wasOpenSuccessful = false; + $realPathURI = $this->convertURIToUseRealPath($URI); // HHVM does not check if file exists within zip file // @link https://github.com/facebook/hhvm/issues/5779 - if ($this->isRunningHHVM() && $this->isZipStream($URI)) { - if ($this->fileExistsWithinZip($URI)) { - $wasOpenSuccessful = parent::open($URI, $encoding, $options|LIBXML_NONET); + if ($this->isRunningHHVM() && $this->isZipStream($realPathURI)) { + if ($this->fileExistsWithinZip($realPathURI)) { + $wasOpenSuccessful = parent::open($realPathURI, $encoding, $options|LIBXML_NONET); } } else { - $wasOpenSuccessful = parent::open($URI, $encoding, $options|LIBXML_NONET); + $wasOpenSuccessful = parent::open($realPathURI, $encoding, $options|LIBXML_NONET); } return $wasOpenSuccessful; } + /** + * Updates the given URI to use a real path. + * This is to avoid issues on some Windows setup. + * + * @param string $URI URI + * @return string The URI using a real path + */ + protected function convertURIToUseRealPath($URI) + { + $realPathURI = $URI; + + if ($this->isZipStream($URI)) { + if (preg_match('/zip:\/\/(.*)#(.*)/', $URI, $matches)) { + $documentPath = $matches[1]; + $documentInsideZipPath = $matches[2]; + $realPathURI = 'zip://' . realpath($documentPath) . '#' . $documentInsideZipPath; + } + } else { + $realPathURI = realpath($URI); + } + + return $realPathURI; + } + /** * Returns whether the given URI is a zip stream. * diff --git a/tests/Spout/Reader/Wrapper/XMLReaderTest.php b/tests/Spout/Reader/Wrapper/XMLReaderTest.php index 097a5fa7..a4deacdb 100644 --- a/tests/Spout/Reader/Wrapper/XMLReaderTest.php +++ b/tests/Spout/Reader/Wrapper/XMLReaderTest.php @@ -163,4 +163,39 @@ public function testFileExistsWithinZip($innerFilePath, $expectedResult) $this->assertEquals($expectedResult, $isZipStream); } + + /** + * @return array + */ + public function dataProviderForTestConvertURIToUseRealPath() + { + $tempFolder = realpath(sys_get_temp_dir()); + + return [ + ['/../../../' . $tempFolder . '/test.xlsx', $tempFolder . '/test.xlsx'], + [$tempFolder . '/test.xlsx', $tempFolder . '/test.xlsx'], + ['zip://' . $tempFolder . '/test.xlsx#test.xml', 'zip://' . $tempFolder . '/test.xlsx#test.xml'], + ['zip:///../../../' . $tempFolder . '/test.xlsx#test.xml', 'zip://' . $tempFolder . '/test.xlsx#test.xml'], + ]; + } + + /** + * @dataProvider dataProviderForTestConvertURIToUseRealPath + * + * @param string $URI + * @param string $expectedConvertedURI + * @return void + */ + public function testConvertURIToUseRealPath($URI, $expectedConvertedURI) + { + $tempFolder = sys_get_temp_dir(); + touch($tempFolder . '/test.xlsx'); + + $xmlReader = new XMLReader(); + $convertedURI = \ReflectionHelper::callMethodOnObject($xmlReader, 'convertURIToUseRealPath', $URI); + + $this->assertEquals($expectedConvertedURI, $convertedURI); + + unlink($tempFolder . '/test.xlsx'); + } } diff --git a/tests/Spout/TestUsingResource.php b/tests/Spout/TestUsingResource.php index d072159f..ec183b90 100644 --- a/tests/Spout/TestUsingResource.php +++ b/tests/Spout/TestUsingResource.php @@ -10,13 +10,13 @@ trait TestUsingResource { /** @var string Path to the test resources folder */ - private $resourcesPath = 'tests/resources/'; + private $resourcesPath = 'tests/resources'; /** @var string Path to the test generated resources folder */ - private $generatedResourcesPath = 'tests/resources/generated/'; + private $generatedResourcesPath = 'tests/resources/generated'; /** @var string Path to the test resources folder, that does not have writing permissions */ - private $generatedUnwritableResourcesPath = 'tests/resources/generated/unwritable/'; + private $generatedUnwritableResourcesPath = 'tests/resources/generated/unwritable'; /** * @param string $resourceName @@ -25,7 +25,7 @@ trait TestUsingResource { protected function getResourcePath($resourceName) { $resourceType = pathinfo($resourceName, PATHINFO_EXTENSION); - $resourcePath = $this->resourcesPath . strtolower($resourceType) . '/' . $resourceName; + $resourcePath = realpath($this->resourcesPath) . '/' . strtolower($resourceType) . '/' . $resourceName; return (file_exists($resourcePath) ? $resourcePath : null); } @@ -37,7 +37,7 @@ protected function getResourcePath($resourceName) protected function getGeneratedResourcePath($resourceName) { $resourceType = pathinfo($resourceName, PATHINFO_EXTENSION); - $generatedResourcePath = $this->generatedResourcesPath . strtolower($resourceType) . '/' . $resourceName; + $generatedResourcePath = realpath($this->generatedResourcesPath) . '/' . strtolower($resourceType) . '/' . $resourceName; return $generatedResourcePath; } @@ -49,7 +49,7 @@ protected function getGeneratedResourcePath($resourceName) protected function createGeneratedFolderIfNeeded($resourceName) { $resourceType = pathinfo($resourceName, PATHINFO_EXTENSION); - $generatedResourcePathForType = $this->generatedResourcesPath . strtolower($resourceType); + $generatedResourcePathForType = $this->generatedResourcesPath . '/' . strtolower($resourceType); if (!file_exists($generatedResourcePathForType)) { mkdir($generatedResourcePathForType, 0777, true); @@ -62,7 +62,7 @@ protected function createGeneratedFolderIfNeeded($resourceName) */ protected function getGeneratedUnwritableResourcePath($resourceName) { - return $this->generatedUnwritableResourcesPath . $resourceName; + return realpath($this->generatedUnwritableResourcesPath) . '/' . $resourceName; } /**