Skip to content

Commit

Permalink
[LazyStreamWriter] Add metadata fetching with `LazyStreamWriter::getM…
Browse files Browse the repository at this point in the history
…etadata()` method
  • Loading branch information
alexandre-daubois committed Jun 20, 2023
1 parent 72a9532 commit 9bd3039
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 19 deletions.
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ composer require alexandre-daubois/lazy-stream

## Usage

### Writing lazily to a stream
### Writing lazily to a stream with `LazyStreamWriter`

```php
function provideJsonData(): \Generator
Expand All @@ -49,9 +49,20 @@ $stream = new \LazyStream\LazyStreamWriter(
// Trigger the stream to *actually* initiate connection
// and unwrap the iterator
$stream->trigger();

// Fetch stream's metadata, which will also be done lazily. It is
// *not* required to call `trigger()` to get those data.
$metadata = $stream->getMetadata();
```

### Usage with third-party libraries
### Configuring `LazyStreamWriter` behavior

A few options are available to configure how à lazy stream should behave:

- Opening mode: this allows to define the mode that will be used to open the stream. Any writing mode [listed here](https://www.php.net/manual/en/function.fopen.php) can be used.
- Auto-closing: whether the stream should be automatically flushed and closed at the end of the `trigger()` method. If set to false, the stream will be flushed and closed in any case when the `LazyStreamWriter` object is destroyed.

## Usage with third-party libraries

This library also works well with third-party libraries. For example, you can combine it with the [google/cloud-storage](https://packagist.org/packages/google/cloud-storage) package to write big files to your buckets without having to worry about memory problems (among other things).

Expand Down
63 changes: 46 additions & 17 deletions src/LazyStreamWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,30 @@ class LazyStreamWriter implements LazyStreamWriterInterface
*/
private $handle;

private ?array $metadata = null;

/**
* @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 string $openingMode 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 string $openMode = 'w',
private string $openingMode = 'w',
private bool $autoClose = true,
) {
}

public function __destruct()
{
if (\is_resource($this->handle)) {
\fclose($this->handle);
}
$this->closeStream();
}

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

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

try {
while ($this->dataProvider->valid()) {
Expand All @@ -66,10 +60,8 @@ public function trigger(): void
} catch (\Throwable $throwable) {
throw new LazyStreamWriterTriggerException(previous: $throwable);
} finally {
if (\is_resource($this->handle) && $this->autoClose) {
\fclose($this->handle);

$this->handle = null;
if ($this->autoClose) {
$this->closeStream();
}
}
}
Expand All @@ -88,11 +80,25 @@ public function unlink(): bool
return true;
}

$this->handle = null;
$this->closeStream();

return \unlink($this->uri);
}

/**
* @return array Stream meta-data array indexed by keys given in https://www.php.net/manual/en/function.stream-get-meta-data.php.
*/
public function getMetadata(): array
{
if ($this->metadata === null) {
// If metadata is null, then we never opened the stream yet
$this->openStream();
$this->closeStream();
}

return $this->metadata;
}

public function equals(self $other): bool
{
return $this->dataProvider === $other->dataProvider && $this->uri === $other->uri;
Expand All @@ -107,4 +113,27 @@ public function setAutoClose(bool $autoClose): void
{
$this->autoClose = $autoClose;
}

private function openStream(): void
{
if (!\is_resource($this->handle)) {
$this->handle = @\fopen($this->uri, $this->openingMode);

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

$this->metadata = \stream_get_meta_data($this->handle);
}

private function closeStream(): void
{
if (\is_resource($this->handle)) {
\fflush($this->handle);
\fclose($this->handle);

$this->handle = null;
}
}
}
29 changes: 29 additions & 0 deletions tests/LazyStreamWriterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,33 @@ public function testTriggersThrowsOnUnwrappingWithoutAutoClose(): void
throw $exception;
}
}

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

$this->assertSame('MEMORY', $lazyStream->getMetadata()['stream_type']);
$this->assertNull($lazyStream->getStreamHandle());
}

public function testGetTypeOnTriggeredStreamWithoutAutoclose(): void
{
$lazyStream = new LazyStreamWriter('php://memory', new \ArrayIterator([]), autoClose: false);

$lazyStream->trigger();

$this->assertSame('MEMORY', $lazyStream->getMetadata()['stream_type']);
$this->assertNotNull($lazyStream->getStreamHandle());
}

public function testGetTypeOnTriggeredStreamWithAutoclose(): void
{
$lazyStream = new LazyStreamWriter('php://memory', new \ArrayIterator([]), autoClose: true);

$lazyStream->trigger();
$this->assertNull($lazyStream->getStreamHandle());

$this->assertSame('MEMORY', $lazyStream->getMetadata()['stream_type']);
$this->assertNull($lazyStream->getStreamHandle());
}
}

0 comments on commit 9bd3039

Please sign in to comment.