Skip to content

Commit

Permalink
Fix issue with Stream
Browse files Browse the repository at this point in the history
fopen would fail to create a file if it didn't exist in write mode.
However touch was working for same file
Locator returned null because it couldn't find the file to be created
All can't be set on read because you do want to read across all sprinkle
In write mode, you'll write to the first location anyways
  • Loading branch information
lcharette committed Oct 25, 2023
1 parent 6078a01 commit b97e4a2
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 5 deletions.
13 changes: 10 additions & 3 deletions src/UniformResourceLocator/StreamWrapper/Stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,23 @@ public static function setLocator(ResourceLocatorInterface $locator): void
*/
public function stream_open(string $uri, string $mode, int $options, ?string &$opened_path): bool
{
$path = $this->findPath($uri);
// In write mode, we want to write to the first existing path (should
// be a shared location) because fopen will attempts to create the file.
// Otherwise, we need to find the first found path, across location.
if (in_array($mode, ['w', 'w+', 'a', 'a+', 'x', 'x+'], true)) {
$path = $this->findPath($uri, true);
} else {
$path = $this->findPath($uri);
}

if ($path === null) {
return false;
}

$handle = @fopen($path, $mode);

// fopen will return false if mode is 'x' and file already exist.
// See : https://www.php.net/manual/en/function.fopen
// fopen will return false if file is not found or if mode is 'x' and
// file already exist. See : https://www.php.net/manual/en/function.fopen
if ($handle === false) {
return false;
}
Expand Down
60 changes: 58 additions & 2 deletions tests/UniformResourceLocator/StreamWrapper/StreamTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,73 @@ public function testChmod(): void
}

/**
* Test on a file that already exist.
* @depends testSimpleFile
*/
public function testFOpen(): void
{
touch($this->file);
$this->assertTrue(file_exists($this->file)); // Make sure file exist, it's the point of the test
$this->assertIsResource(fopen($this->file, 'w'));
unlink($this->file);
}

/**
* Make sure fopen create the file if it doesn't exist.
* @depends testSimpleFile
*/
public function testFOpenFileNotExistButWillCreate(): void
{
$this->assertFalse(file_exists($this->file));
$this->assertIsResource(fopen($this->file, 'w'));
$this->assertTrue(file_exists($this->file));
unlink($this->file);
}

/**
* Make sure fopen will return false if the file doesn't exist in readonly
* mode. Even if locator return a resource path (with all flag).
*
* @depends testSimpleFile
*/
public function testFOpenFileNotExist(): void
{
// stream_open will trigger an error even if the stream return false.
// fopen(bar://test.txt): Failed to open stream: "UserFrosting\UniformResourceLocator\StreamWrapper\Stream::stream_open" call failed
// This suppress this error, and allow us to test the return value.
set_error_handler(function ($no, $str, $file, $line) { // @phpstan-ignore-line
});

touch($this->file); // Touch basic file
$this->assertFalse(file_exists($this->file));
$this->assertFalse(fopen($this->file, 'r'));
$this->assertFalse(file_exists($this->file));
}

/**
* @depends testSimpleFile
*/
public function testFOpenInvalidPath(): void
{
// Catch exception
set_error_handler(function ($no, $str, $file, $line) { // @phpstan-ignore-line
});

$this->assertFalse(fopen('bar://test.txt', 'w'));
}

/**
* fopen will return false if mode is 'x' and file already exist.
* @depends testSimpleFile
*/
public function testFOpenFalseReturn(): void
{
// Catch exception
set_error_handler(function ($no, $str, $file, $line) { // @phpstan-ignore-line
});

touch($this->file);
$this->assertFalse(fopen($this->file, 'x'));
unlink($this->file); // Reset state
unlink($this->file);
}

/**
Expand Down

0 comments on commit b97e4a2

Please sign in to comment.