Skip to content

Commit

Permalink
Disallow cloning Stream objects
Browse files Browse the repository at this point in the history
Streams hold a reference to the stateful resource handle for their actual
contents. Cloning a Stream will not actually clone the underlying resource,
thus both streams would still refer to the same resource after cloning and any
changes in one stream object would be reflected in the other object. This
violates user expectations after a cloning operation.

Disallow cloning entirely as the safe default choice. Alternatively a new
stream could be created and attached and the contents could be copied over.
This can get expensive with larger or infinite streams, though.

Signed-off-by: Tim Düsterhus <duesterhus@woltlab.com>
  • Loading branch information
TimWolla committed Apr 13, 2023
1 parent 13f45e5 commit e3db859
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/Stream.php
Expand Up @@ -364,4 +364,11 @@ private function isValidStreamResourceType(mixed $resource): bool

return false;
}

/**
* Disallow stream cloning.
*/
private function __clone()
{
}
}
15 changes: 15 additions & 0 deletions test/StreamTest.php
Expand Up @@ -5,6 +5,7 @@
namespace LaminasTest\Diactoros;

use CurlHandle;
use Error;
use GdImage;
use InvalidArgumentException;
use Laminas\Diactoros\Stream;
Expand Down Expand Up @@ -679,4 +680,18 @@ public function testSizeReportsNullForPhpInputStreams(): void
$stream = new Stream($resource);
$this->assertNull($stream->getSize());
}

public function testStreamsAreUnclonable(): void
{
$stream = new Stream(fopen('php://temp', 'r+'));
$stream->write('foo');

$this->assertSame('foo', $stream->__toString());

$this->expectException(Error::class);
$this->expectExceptionMessage('private Laminas\Diactoros\Stream::__clone()');

/** @psalm-suppress InvalidClone */
clone $stream;
}
}

0 comments on commit e3db859

Please sign in to comment.