Skip to content

Instantly share code, notes, and snippets.

@sasezaki
Last active August 29, 2015 14:22
Show Gist options
  • Save sasezaki/72e77f04a47bed7db951 to your computer and use it in GitHub Desktop.
Save sasezaki/72e77f04a47bed7db951 to your computer and use it in GitHub Desktop.
memory usage echo vs stream_copy_to_stream when 10M send
$ curl "http://localhost/tmp/diactoros/test.php?bigsize" > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 10.0M 100 10.0M 0 0 35.9M 0 --:--:-- --:--:-- --:--:-- 35.9M
$ cat memory_get_usage.txt
10900304
$ curl "http://localhost/tmp/diactoros/test.php?emitter=streamcopy&bigsize" > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 10.0M 100 10.0M 0 0 31.6M 0 --:--:-- --:--:-- --:--:-- 31.6M
$ cat memory_get_usage.txt
423736
<?php
use Zend\Diactoros\Response;
use Zend\Diactoros\Stream;
use Zend\Diactoros\Response\SapiEmitter;
use Zend\Diactoros\ServerRequestFactory;
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/StreamCopyEmitter.php';
$request = ServerRequestFactory::fromGlobals();
$response = new Response();
if (isset($_GET['emitter']) && $_GET['emitter'] == 'streamcopy') {
$emitter = new StreamCopyEmitter();
} else {
$emitter = new SapiEmitter();
}
if (isset($_GET['bigsize'])) {
// $ dd if=/dev/zero of=tempfile bs=1M count=10
$image = __DIR__.'/tempfile';
$response = $response->withHeader('Content-Type', 'image/jpeg')
->withHeader('Content-Length', (string) filesize($image))
->withBody(new Stream($image));
} else {
$response->getBody()->write("hello");
}
$emitter->emit($response);
file_put_contents("memory_get_usage.txt", var_export(memory_get_usage(), true));
<?php
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\EmitterInterface;
//use RuntimeException;
class StreamCopyEmitter implements EmitterInterface
{
/**
* Emits a response for a PHP SAPI environment.
*
* Emits the status line and headers via the header() function, and the
* body content via the output buffer.
*
* @param ResponseInterface $response
* @param null|int $maxBufferLevel Maximum output buffering level to unwrap.
*/
public function emit(ResponseInterface $response, $maxBufferLevel = null)
{
if (headers_sent()) {
throw new RuntimeException('Unable to emit response; headers already sent');
}
$this->emitStatusLine($response);
$this->emitHeaders($response);
$this->emitBody($response, $maxBufferLevel);
}
/**
* Emit the status line.
*
* Emits the status line using the protocol version and status code from
* the response; if a reason phrase is availble, it, too, is emitted.
*
* @param ResponseInterface $response
*/
private function emitStatusLine(ResponseInterface $response)
{
$reasonPhrase = $response->getReasonPhrase();
header(sprintf(
'HTTP/%s %d%s',
$response->getProtocolVersion(),
$response->getStatusCode(),
($reasonPhrase ? ' ' . $reasonPhrase : '')
));
}
/**
* Emit response headers.
*
* Loops through each header, emitting each; if the header value
* is an array with multiple values, ensures that each is sent
* in such a way as to create aggregate headers (instead of replace
* the previous).
*
* @param ResponseInterface $response
*/
private function emitHeaders(ResponseInterface $response)
{
foreach ($response->getHeaders() as $header => $values) {
$name = $this->filterHeader($header);
$first = true;
foreach ($values as $value) {
header(sprintf(
'%s: %s',
$name,
$value
), $first);
$first = false;
}
}
}
/**
* Emit the message body.
*
* Loops through the output buffer, flushing each, before emitting
* the response body using `echo()`.
*
* @param ResponseInterface $response
* @param int $maxBufferLevel Flush up to this buffer level.
*/
private function emitBody(ResponseInterface $response, $maxBufferLevel)
{
if (null === $maxBufferLevel) {
$maxBufferLevel = ob_get_level();
}
while (ob_get_level() > $maxBufferLevel) {
ob_end_flush();
}
//echo $response->getBody();
$fp = $response->getBody()->detach();
rewind($fp);
stream_copy_to_stream($fp, fopen('php://output', 'w'));
}
/**
* Filter a header name to wordcase
*
* @param string $header
* @return string
*/
private function filterHeader($header)
{
$filtered = str_replace('-', ' ', $header);
$filtered = ucwords($filtered);
return str_replace(' ', '-', $filtered);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment