Skip to content
This repository was archived by the owner on May 26, 2022. It is now read-only.

Commit d1c4d56

Browse files
committed
Merge pull request #126 from box/fix_xmlreader_open_issue_windows
Fix "Cannot open file" issue with XMLReader::open on Windows
2 parents 4598019 + 01cc8b3 commit d1c4d56

File tree

5 files changed

+111
-13
lines changed

5 files changed

+111
-13
lines changed

src/Spout/Common/Helper/GlobalFunctionsHelper.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,43 @@ public function file_exists($fileName)
164164
*/
165165
public function file_get_contents($filePath)
166166
{
167-
return file_get_contents($filePath);
167+
$realFilePath = $this->convertToUseRealPath($filePath);
168+
return file_get_contents($realFilePath);
169+
}
170+
171+
/**
172+
* Updates the given file path to use a real path.
173+
* This is to avoid issues on some Windows setup.
174+
*
175+
* @param string $filePath File path
176+
* @return string The file path using a real path
177+
*/
178+
protected function convertToUseRealPath($filePath)
179+
{
180+
$realFilePath = $filePath;
181+
182+
if ($this->isZipStream($filePath)) {
183+
if (preg_match('/zip:\/\/(.*)#(.*)/', $filePath, $matches)) {
184+
$documentPath = $matches[1];
185+
$documentInsideZipPath = $matches[2];
186+
$realFilePath = 'zip://' . realpath($documentPath) . '#' . $documentInsideZipPath;
187+
}
188+
} else {
189+
$realFilePath = realpath($filePath);
190+
}
191+
192+
return $realFilePath;
193+
}
194+
195+
/**
196+
* Returns whether the given path is a zip stream.
197+
*
198+
* @param string $path Path pointing to a document
199+
* @return bool TRUE if path is a zip stream, FALSE otherwise
200+
*/
201+
protected function isZipStream($path)
202+
{
203+
return (strpos($path, 'zip://') === 0);
168204
}
169205

170206
/**

src/Spout/Reader/AbstractReader.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ public function open($filePath)
7272
}
7373

7474
try {
75-
$this->openReader($filePath);
75+
// Need to use realpath to fix "Can't open file" on some Windows setup
76+
$fileRealPath = realpath($filePath);
77+
$this->openReader($fileRealPath);
7678
$this->isStreamOpened = true;
7779
} catch (\Exception $exception) {
7880
throw new IOException("Could not open $filePath for reading! ({$exception->getMessage()})");

src/Spout/Reader/Wrapper/XMLReader.php

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,45 @@ class XMLReader extends \XMLReader
2626
public function open($URI, $encoding = null, $options = 0)
2727
{
2828
$wasOpenSuccessful = false;
29+
$realPathURI = $this->convertURIToUseRealPath($URI);
2930

3031
// HHVM does not check if file exists within zip file
3132
// @link https://github.com/facebook/hhvm/issues/5779
32-
if ($this->isRunningHHVM() && $this->isZipStream($URI)) {
33-
if ($this->fileExistsWithinZip($URI)) {
34-
$wasOpenSuccessful = parent::open($URI, $encoding, $options|LIBXML_NONET);
33+
if ($this->isRunningHHVM() && $this->isZipStream($realPathURI)) {
34+
if ($this->fileExistsWithinZip($realPathURI)) {
35+
$wasOpenSuccessful = parent::open($realPathURI, $encoding, $options|LIBXML_NONET);
3536
}
3637
} else {
37-
$wasOpenSuccessful = parent::open($URI, $encoding, $options|LIBXML_NONET);
38+
$wasOpenSuccessful = parent::open($realPathURI, $encoding, $options|LIBXML_NONET);
3839
}
3940

4041
return $wasOpenSuccessful;
4142
}
4243

44+
/**
45+
* Updates the given URI to use a real path.
46+
* This is to avoid issues on some Windows setup.
47+
*
48+
* @param string $URI URI
49+
* @return string The URI using a real path
50+
*/
51+
protected function convertURIToUseRealPath($URI)
52+
{
53+
$realPathURI = $URI;
54+
55+
if ($this->isZipStream($URI)) {
56+
if (preg_match('/zip:\/\/(.*)#(.*)/', $URI, $matches)) {
57+
$documentPath = $matches[1];
58+
$documentInsideZipPath = $matches[2];
59+
$realPathURI = 'zip://' . realpath($documentPath) . '#' . $documentInsideZipPath;
60+
}
61+
} else {
62+
$realPathURI = realpath($URI);
63+
}
64+
65+
return $realPathURI;
66+
}
67+
4368
/**
4469
* Returns whether the given URI is a zip stream.
4570
*

tests/Spout/Reader/Wrapper/XMLReaderTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,39 @@ public function testFileExistsWithinZip($innerFilePath, $expectedResult)
163163

164164
$this->assertEquals($expectedResult, $isZipStream);
165165
}
166+
167+
/**
168+
* @return array
169+
*/
170+
public function dataProviderForTestConvertURIToUseRealPath()
171+
{
172+
$tempFolder = realpath(sys_get_temp_dir());
173+
174+
return [
175+
['/../../../' . $tempFolder . '/test.xlsx', $tempFolder . '/test.xlsx'],
176+
[$tempFolder . '/test.xlsx', $tempFolder . '/test.xlsx'],
177+
['zip://' . $tempFolder . '/test.xlsx#test.xml', 'zip://' . $tempFolder . '/test.xlsx#test.xml'],
178+
['zip:///../../../' . $tempFolder . '/test.xlsx#test.xml', 'zip://' . $tempFolder . '/test.xlsx#test.xml'],
179+
];
180+
}
181+
182+
/**
183+
* @dataProvider dataProviderForTestConvertURIToUseRealPath
184+
*
185+
* @param string $URI
186+
* @param string $expectedConvertedURI
187+
* @return void
188+
*/
189+
public function testConvertURIToUseRealPath($URI, $expectedConvertedURI)
190+
{
191+
$tempFolder = sys_get_temp_dir();
192+
touch($tempFolder . '/test.xlsx');
193+
194+
$xmlReader = new XMLReader();
195+
$convertedURI = \ReflectionHelper::callMethodOnObject($xmlReader, 'convertURIToUseRealPath', $URI);
196+
197+
$this->assertEquals($expectedConvertedURI, $convertedURI);
198+
199+
unlink($tempFolder . '/test.xlsx');
200+
}
166201
}

tests/Spout/TestUsingResource.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
trait TestUsingResource {
1111

1212
/** @var string Path to the test resources folder */
13-
private $resourcesPath = 'tests/resources/';
13+
private $resourcesPath = 'tests/resources';
1414

1515
/** @var string Path to the test generated resources folder */
16-
private $generatedResourcesPath = 'tests/resources/generated/';
16+
private $generatedResourcesPath = 'tests/resources/generated';
1717

1818
/** @var string Path to the test resources folder, that does not have writing permissions */
19-
private $generatedUnwritableResourcesPath = 'tests/resources/generated/unwritable/';
19+
private $generatedUnwritableResourcesPath = 'tests/resources/generated/unwritable';
2020

2121
/**
2222
* @param string $resourceName
@@ -25,7 +25,7 @@ trait TestUsingResource {
2525
protected function getResourcePath($resourceName)
2626
{
2727
$resourceType = pathinfo($resourceName, PATHINFO_EXTENSION);
28-
$resourcePath = $this->resourcesPath . strtolower($resourceType) . '/' . $resourceName;
28+
$resourcePath = realpath($this->resourcesPath) . '/' . strtolower($resourceType) . '/' . $resourceName;
2929

3030
return (file_exists($resourcePath) ? $resourcePath : null);
3131
}
@@ -37,7 +37,7 @@ protected function getResourcePath($resourceName)
3737
protected function getGeneratedResourcePath($resourceName)
3838
{
3939
$resourceType = pathinfo($resourceName, PATHINFO_EXTENSION);
40-
$generatedResourcePath = $this->generatedResourcesPath . strtolower($resourceType) . '/' . $resourceName;
40+
$generatedResourcePath = realpath($this->generatedResourcesPath) . '/' . strtolower($resourceType) . '/' . $resourceName;
4141

4242
return $generatedResourcePath;
4343
}
@@ -49,7 +49,7 @@ protected function getGeneratedResourcePath($resourceName)
4949
protected function createGeneratedFolderIfNeeded($resourceName)
5050
{
5151
$resourceType = pathinfo($resourceName, PATHINFO_EXTENSION);
52-
$generatedResourcePathForType = $this->generatedResourcesPath . strtolower($resourceType);
52+
$generatedResourcePathForType = $this->generatedResourcesPath . '/' . strtolower($resourceType);
5353

5454
if (!file_exists($generatedResourcePathForType)) {
5555
mkdir($generatedResourcePathForType, 0777, true);
@@ -62,7 +62,7 @@ protected function createGeneratedFolderIfNeeded($resourceName)
6262
*/
6363
protected function getGeneratedUnwritableResourcePath($resourceName)
6464
{
65-
return $this->generatedUnwritableResourcesPath . $resourceName;
65+
return realpath($this->generatedUnwritableResourcesPath) . '/' . $resourceName;
6666
}
6767

6868
/**

0 commit comments

Comments
 (0)