Skip to content

Commit

Permalink
[LazyStreamWriter] Add open mode, auto close and exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandre-daubois committed Jun 19, 2023
1 parent 639243f commit 097e80e
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 9 deletions.
7 changes: 7 additions & 0 deletions src/Exception/AbstractLazyStreamWriterException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace LazyStream\Exception;

class AbstractLazyStreamWriterException extends \RuntimeException
{
}
11 changes: 11 additions & 0 deletions src/Exception/LazyStreamWriterOpenException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace LazyStream\Exception;

class LazyStreamWriterOpenException extends AbstractLazyStreamWriterException
{
public function __construct(string $uri, string $mode)
{
parent::__construct(sprintf('Unable to open "%s" with mode "%s".', $uri, $mode));
}
}
7 changes: 7 additions & 0 deletions src/Exception/LazyStreamWriterTriggerException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace LazyStream\Exception;

class LazyStreamWriterTriggerException extends AbstractLazyStreamWriterException
{
}
49 changes: 41 additions & 8 deletions src/LazyStreamWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

namespace LazyStream;

use LazyStream\Exception\LazyStreamWriterOpenException;
use LazyStream\Exception\LazyStreamWriterTriggerException;

/**
* A class to write to a stream lazily. The stream is only opened when
* the `trigger()` method is called. Data to write are provided by a
Expand All @@ -21,33 +24,53 @@ class LazyStreamWriter implements LazyStreamWriterInterface
*/
private $handle;

/**
* @param string $uri A valid stream URI.
* @param \Iterator $dataProvider The data provider that will be written to the stream.
* @param string $openMode A valid writing mode listed in https://www.php.net/manual/fr/function.fopen.php.
* @param bool $autoClose Whether the stream should be closed once the `trigger` method is done.
*/
public function __construct(
private string $uri,
private \Iterator $dataProvider
private \Iterator $dataProvider,
private string $openMode = 'w',
private bool $autoClose = true,
) {
}

public function __destruct()
{
if (\is_resource($this->handle)) {
\fflush($this->handle);

\fclose($this->handle);
}
}

public function trigger(): void
{
if (!\is_resource($this->handle)) {
$this->handle = \fopen($this->uri, 'w');
$this->handle = @\fopen($this->uri, $this->openMode);

if ($this->handle === false) {
throw new LazyStreamWriterOpenException($this->uri, $this->openMode);
}
}

while ($this->dataProvider->valid()) {
$data = $this->dataProvider->current();
try {
while ($this->dataProvider->valid()) {
$data = $this->dataProvider->current();

\fwrite($this->handle, $data, \strlen($data));

\fwrite($this->handle, $data, \strlen($data));
$this->dataProvider->next();
}
} catch (\Throwable $throwable) {
throw new LazyStreamWriterTriggerException(previous: $throwable);
} finally {
if (\is_resource($this->handle) && $this->autoClose) {
\fclose($this->handle);

$this->dataProvider->next();
$this->handle = null;
}
}
}

Expand All @@ -74,4 +97,14 @@ public function equals(self $other): bool
{
return $this->dataProvider === $other->dataProvider && $this->uri === $other->uri;
}

public function isAutoClose(): bool
{
return $this->autoClose;
}

public function setAutoClose(bool $autoClose): void
{
$this->autoClose = $autoClose;
}
}
54 changes: 53 additions & 1 deletion tests/LazyStreamWriterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@

namespace LazyStream\Tests;

use LazyStream\Exception\LazyStreamWriterOpenException;
use LazyStream\Exception\LazyStreamWriterTriggerException;
use LazyStream\LazyStreamWriter;
use PHPUnit\Framework\TestCase;
use Traversable;

/**
* @covers \LazyStream\LazyStreamWriter
Expand Down Expand Up @@ -60,7 +63,7 @@ public function testTriggerStreams(): void
yield 'chunk';

return 'return_value';
})());
})(), autoClose: false);

$lazyStream->trigger();
$handle = $lazyStream->getStreamHandle();
Expand All @@ -72,4 +75,53 @@ public function testTriggerStreams(): void
// Generator should be closed
$this->assertFalse($generator->valid());
}

public function testInvalidStream(): void
{
$lazyStream = new LazyStreamWriter('php://invalid', new \ArrayIterator([]));

$this->expectException(LazyStreamWriterOpenException::class);
$this->expectExceptionMessage('Unable to open "php://invalid" with mode "w".');
$lazyStream->trigger();
}

public function testTriggersThrowsOnUnwrappingWithAutoClose(): void
{
$expectedException = new \Exception();

$lazyStream = new LazyStreamWriter('php://memory', (static function () use ($expectedException): \Generator {
yield 'data';

throw $expectedException;
})());

$this->expectExceptionObject(new LazyStreamWriterTriggerException(previous: $expectedException));
try {
$lazyStream->trigger();
} catch (\Exception $exception) {
$this->assertNull($lazyStream->getStreamHandle());

throw $exception;
}
}

public function testTriggersThrowsOnUnwrappingWithoutAutoClose(): void
{
$expectedException = new \Exception();

$lazyStream = new LazyStreamWriter('php://memory', (static function () use ($expectedException): \Generator {
yield 'data';

throw $expectedException;
})(), autoClose: false);

$this->expectExceptionObject(new LazyStreamWriterTriggerException(previous: $expectedException));
try {
$lazyStream->trigger();
} catch (\Exception $exception) {
$this->assertNotNull($lazyStream->getStreamHandle());

throw $exception;
}
}
}

0 comments on commit 097e80e

Please sign in to comment.