-
Notifications
You must be signed in to change notification settings - Fork 39
Add Message cloner #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
namespace spec\Http\Message; | ||
|
||
use PhpSpec\ObjectBehavior; | ||
use Prophecy\Argument; | ||
use Psr\Http\Message\MessageInterface; | ||
use spec\Http\Message\Encoding\MemoryStream; | ||
|
||
class MessageClonerSpec extends ObjectBehavior | ||
{ | ||
public function it_is_initializable() | ||
{ | ||
$this->shouldHaveType('Http\Message\MessageCloner'); | ||
} | ||
|
||
public function it_clone_a_message(MessageInterface $message, MessageInterface $messageCloned) | ||
{ | ||
$stream = new MemoryStream('test'); | ||
$message->getBody()->willReturn($stream); | ||
$message->withBody(Argument::type('Http\Message\MemoryClonedStream'))->willReturn($messageCloned); | ||
|
||
$this->cloneMessage($message)->shouldEqual($messageCloned); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
<?php | ||
|
||
namespace Http\Message; | ||
|
||
use Psr\Http\Message\StreamInterface; | ||
|
||
/** | ||
* Represent a stream cloned into memory. | ||
*/ | ||
class MemoryClonedStream implements StreamInterface | ||
{ | ||
const COPY_BUFFER = 8192; | ||
|
||
private $resource; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add type hints here too for internal usage? |
||
|
||
private $size; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here |
||
|
||
/** | ||
* @param StreamInterface $stream Stream to clone | ||
* @param bool $useFileBuffer Use a file buffer to avoid memory consumption on PHP script (default to true) | ||
* @param int $memoryBuffer The amount of memory of which the stream is buffer into a file when setting | ||
* $useFileBuffer to true (default to 2MB) | ||
*/ | ||
public function __construct(StreamInterface $stream, $useFileBuffer = true, $memoryBuffer = 2097152) | ||
{ | ||
$this->size = 0; | ||
|
||
if ($useFileBuffer) { | ||
$this->resource = fopen('php://temp/maxmemory:'.$memoryBuffer, 'rw+'); | ||
} else { | ||
$this->resource = fopen('php://memory', 'rw+'); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe verify that $this->resource is not false. |
||
$position = null; | ||
|
||
if ($stream->isSeekable()) { | ||
$position = $stream->tell(); | ||
$stream->rewind(); | ||
} | ||
|
||
while (!$stream->eof()) { | ||
$this->size += fwrite($this->resource, $stream->read(self::COPY_BUFFER)); | ||
} | ||
|
||
if ($stream->isSeekable()) { | ||
$stream->seek($position); | ||
} | ||
|
||
rewind($this->resource); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add __destruct public function __destruct()
{
$this->close();
} |
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function __toString() | ||
{ | ||
return $this->getContents(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should rewind it first |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function close() | ||
{ | ||
fclose($this->resource); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function detach() | ||
{ | ||
$resource = $this->resource; | ||
$this->resource = null; | ||
|
||
return $resource; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Size should be set to |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getSize() | ||
{ | ||
return $this->size; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function tell() | ||
{ | ||
return ftell($this->resource); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function eof() | ||
{ | ||
return feof($this->resource); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function isSeekable() | ||
{ | ||
return true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return false if $this->resource is null. |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function seek($offset, $whence = SEEK_SET) | ||
{ | ||
return fseek($this->resource, $offset); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think psr7 says you should throw exception of whence is not supported. |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function rewind() | ||
{ | ||
return rewind($this->resource); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function isWritable() | ||
{ | ||
return true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return false if $this->resource is null. |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function write($string) | ||
{ | ||
return fwrite($this->resource, $string); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function isReadable() | ||
{ | ||
return true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return false if $this->resource is null. |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function read($length) | ||
{ | ||
return fread($this->resource, $length); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if |
||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getContents() | ||
{ | ||
$this->rewind(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PSR7 says that getContents should return "the rest of the string" if I remember corectly. Do not rewind here |
||
|
||
return $this->read($this->size); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getMetadata($key = null) | ||
{ | ||
$metadata = stream_get_meta_data($this->resource); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if $this->resource is not null. |
||
|
||
if (null === $key) { | ||
return $metadata; | ||
} | ||
|
||
return $metadata[$key]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Verify with isset |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
namespace Http\Message; | ||
|
||
use Psr\Http\Message\MessageInterface; | ||
|
||
/** | ||
* Allow to clone request and response. | ||
* | ||
* A message cloner is only necessary when you also want to duplicate the stream of a request or a response, as by | ||
* default object returned by `clone $message` call will have the same stream as the cloned one, so reading the body of | ||
* one of the message will affect the other. | ||
*/ | ||
class MessageCloner | ||
{ | ||
/** | ||
* Clone a message. | ||
* | ||
* When cloning you have to be careful that the original stream is rewindable. If not the original stream will be | ||
* readed and cannot be readed one more time. To avoid this behavior, when the original stream is not seekable, you | ||
* can clone the cloned message, and replace the original message with one of the clone, as there are always rewindable. | ||
* | ||
* @param MessageInterface $message | ||
* | ||
* @return MessageInterface|static | ||
*/ | ||
public function cloneMessage(MessageInterface $message) | ||
{ | ||
return $message->withBody(new MemoryClonedStream($message->getBody())); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clones
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$clonedMessage