Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a2f32a9

Browse files
committed
[output-stream] Refactor StreamingResponse implementation to use an OutputStream
1 parent a01dec0 commit a2f32a9

File tree

9 files changed

+225
-1
lines changed

9 files changed

+225
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/HttpKernel.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,11 @@ public function render($controller, array $options = array())
169169
return $response->getContent();
170170
}
171171

172+
$outputStream = $this->container->get('kernel.output_stream');
173+
174+
$response->setOutputStream($outputStream);
172175
$response->sendContent();
176+
$outputStream->close();
173177
} catch (\Exception $e) {
174178
if ($options['alt']) {
175179
$alt = $options['alt'];

src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<parameter key="cache_warmer.class">Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate</parameter>
1212
<parameter key="cache_clearer.class">Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer</parameter>
1313
<parameter key="file_locator.class">Symfony\Component\HttpKernel\Config\FileLocator</parameter>
14+
<parameter key="kernel.output_stream.class">Symfony\Component\HttpFoundation\OutputStream\StreamOutputStream</parameter>
1415
</parameters>
1516

1617
<services>
@@ -51,5 +52,9 @@
5152
<argument type="service" id="kernel" />
5253
<argument>%kernel.root_dir%/Resources</argument>
5354
</service>
55+
56+
<service id="kernel.output_stream" class="%kernel.output_stream.class%" factory-class="%kernel.output_stream.class%" factory-method="create">
57+
<argument>php://output</argument>
58+
</service>
5459
</services>
5560
</container>

src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
<service id="streamed_response_listener" class="%streamed_response_listener.class%">
3434
<tag name="kernel.event_subscriber" />
35+
<argument type="service" id="kernel.output_stream" />
3536
</service>
3637

3738
<service id="locale_listener" class="%locale_listener.class%">
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation\OutputStream;
13+
14+
/**
15+
* OutputStreamInterface defines how streaming responses
16+
* send data to the client.
17+
*
18+
* @author Igor Wiedler <[email protected]>
19+
*/
20+
interface OutputStreamInterface
21+
{
22+
/**
23+
* Write some data to the stream.
24+
*
25+
* @param string $data The data to write
26+
*/
27+
function write($data);
28+
29+
/**
30+
* Close the stream.
31+
*
32+
* This should be called after the sending of data
33+
* has been completed. In case of persistent connections,
34+
* it is never called.
35+
*/
36+
function close();
37+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation\OutputStream;
13+
14+
/**
15+
* StreamOutputStream is an implementation of the OutputStreamInterface
16+
* that uses any writable PHP stream as a target.
17+
*
18+
* Example usage:
19+
*
20+
* $stream = fopen('php://output');
21+
* $output = new StreamOutputStream($stream, 'w');
22+
* $output->write('foo');
23+
* $output->write('bar');
24+
* $output->close();
25+
*
26+
* @author Igor Wiedler <[email protected]>
27+
*/
28+
class StreamOutputStream implements OutputStreamInterface
29+
{
30+
private $stream;
31+
32+
/**
33+
* @param resource $stream A valid stream resource, such as the return value of
34+
* a fopen() call.
35+
*/
36+
public function __construct($stream)
37+
{
38+
if (!is_resource($stream)) {
39+
throw new \InvalidArgumentException('The supplied stream is invalid.');
40+
}
41+
42+
$this->stream = $stream;
43+
}
44+
45+
/**
46+
* @{inheritdoc}
47+
*/
48+
public function write($data)
49+
{
50+
fwrite($this->stream, $data);
51+
}
52+
53+
/**
54+
* @{inheritdoc}
55+
*/
56+
public function close()
57+
{
58+
fclose($this->stream);
59+
}
60+
61+
/**
62+
* Static factory method
63+
*/
64+
static public function create($filename)
65+
{
66+
$stream = fopen($filename, 'w');
67+
return new static($stream);
68+
}
69+
}

src/Symfony/Component/HttpFoundation/StreamedResponse.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\HttpFoundation;
1313

14+
use Symfony\Component\HttpFoundation\OutputStream\OutputStreamInterface;
15+
1416
/**
1517
* StreamedResponse represents a streamed HTTP response.
1618
*
@@ -29,6 +31,7 @@
2931
class StreamedResponse extends Response
3032
{
3133
protected $callback;
34+
protected $outputStream;
3235
protected $streamed;
3336

3437
/**
@@ -71,6 +74,16 @@ public function setCallback($callback)
7174
$this->callback = $callback;
7275
}
7376

77+
/**
78+
* Sets the output stream used for streaming the response.
79+
*
80+
* @param OutputStreamInterface $outputStream stream
81+
*/
82+
public function setOutputStream(OutputStreamInterface $outputStream)
83+
{
84+
$this->outputStream = $outputStream;
85+
}
86+
7487
/**
7588
* @{inheritdoc}
7689
*/
@@ -102,7 +115,11 @@ public function sendContent()
102115
throw new \LogicException('The Response callback must not be null.');
103116
}
104117

105-
call_user_func($this->callback);
118+
if (null === $this->outputStream) {
119+
throw new \LogicException('The Response output stream must not be null.');
120+
}
121+
122+
call_user_func($this->callback, $this->outputStream);
106123
}
107124

108125
/**
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation\Tests\OutputStream;
13+
14+
use Symfony\Component\HttpFoundation\OutputStream\StreamOutputStream;
15+
16+
class OutputStreamTest extends \PHPUnit_Framework_TestCase
17+
{
18+
public function testConstructor()
19+
{
20+
$fp = fopen('php://memory', 'rw');
21+
$output = new StreamOutputStream($fp);
22+
}
23+
24+
/**
25+
* @expectedException InvalidArgumentException
26+
*/
27+
public function testConstructorWithInvalidStream()
28+
{
29+
$fp = 'foobar';
30+
$output = new StreamOutputStream($fp);
31+
}
32+
33+
public function testWrite()
34+
{
35+
$fp = fopen('php://memory', 'rw');
36+
$output = new StreamOutputStream($fp);
37+
38+
$output->write('sample output');
39+
rewind($fp);
40+
$this->assertEquals('sample output', fread($fp, 50));
41+
}
42+
43+
public function testClose()
44+
{
45+
$fp = fopen('php://memory', 'rw');
46+
$output = new StreamOutputStream($fp);
47+
48+
$output->close();
49+
try {
50+
fread($fp, 1);
51+
$this->fail();
52+
} catch (\PHPUnit_Framework_Error_Warning $e) {
53+
}
54+
}
55+
}

src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\HttpFoundation\Request;
1515
use Symfony\Component\HttpFoundation\StreamedResponse;
16+
use Symfony\Component\HttpFoundation\OutputStream\OutputStreamInterface;
1617

1718
class StreamedResponseTest extends \PHPUnit_Framework_TestCase
1819
{
@@ -55,6 +56,7 @@ public function testSendContent()
5556
$called = 0;
5657

5758
$response = new StreamedResponse(function () use (&$called) { ++$called; });
59+
$response->setOutputStream($this->getOutputStreamMock());
5860

5961
$response->sendContent();
6062
$this->assertEquals(1, $called);
@@ -63,12 +65,34 @@ public function testSendContent()
6365
$this->assertEquals(1, $called);
6466
}
6567

68+
public function testSendContentUsesOutputStream()
69+
{
70+
$output = $this->getOutputStreamMock();
71+
$output->expects($this->once())
72+
->method('write')
73+
->with('foo');
74+
75+
$response = new StreamedResponse(function ($output) { $output->write('foo'); });
76+
$response->setOutputStream($output);
77+
$response->sendContent();
78+
}
79+
6680
/**
6781
* @expectedException \LogicException
6882
*/
6983
public function testSendContentWithNonCallable()
7084
{
7185
$response = new StreamedResponse('foobar');
86+
$response->setOutputStream($this->getOutputStreamMock());
87+
$response->sendContent();
88+
}
89+
90+
/**
91+
* @expectedException \LogicException
92+
*/
93+
public function testSendContentWithNoOutputStream()
94+
{
95+
$response = new StreamedResponse(function () {});
7296
$response->sendContent();
7397
}
7498

@@ -94,4 +118,9 @@ public function testCreate()
94118
$this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response);
95119
$this->assertEquals(204, $response->getStatusCode());
96120
}
121+
122+
private function getOutputStreamMock()
123+
{
124+
return $this->getMock('Symfony\Component\HttpFoundation\OutputStream\OutputStreamInterface');
125+
}
97126
}

src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpKernel\EventListener;
1313

1414
use Symfony\Component\HttpFoundation\StreamedResponse;
15+
use Symfony\Component\HttpFoundation\OutputStream\OutputStreamInterface;
1516
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
1617
use Symfony\Component\HttpKernel\HttpKernelInterface;
1718
use Symfony\Component\HttpKernel\KernelEvents;
@@ -25,6 +26,11 @@
2526
*/
2627
class StreamedResponseListener implements EventSubscriberInterface
2728
{
29+
public function __construct(OutputStreamInterface $outputStream)
30+
{
31+
$this->outputStream = $outputStream;
32+
}
33+
2834
/**
2935
* Filters the Response.
3036
*
@@ -39,6 +45,7 @@ public function onKernelResponse(FilterResponseEvent $event)
3945
$response = $event->getResponse();
4046

4147
if ($response instanceof StreamedResponse) {
48+
$response->setOutputStream($this->outputStream);
4249
$response->send();
4350
}
4451
}

0 commit comments

Comments
 (0)