Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit e745d9a

Browse files
committed
Merge branch 'feature/90' into develop
Close #90
2 parents f8391f2 + d0f1765 commit e745d9a

File tree

5 files changed

+382
-2
lines changed

5 files changed

+382
-2
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ All notable changes to this project will be documented in this file, in reverse
88

99
- [#88](https://github.com/zendframework/zend-diactoros/pull/88) updates the
1010
`SapiEmitter` to emit a `Content-Length` header with the content length as
11-
reported by the response body stream.
11+
reported by the response body stream, assuming that
12+
`StreamInterface::getSize()` returns an integer.
1213
- [#77](https://github.com/zendframework/zend-diactoros/pull/77) adds a new
1314
response type, `Zend\Diactoros\Response\TextResponse`, for returning plain
1415
text responses. By default, it sets the content type to `text/plain;
1516
charset=utf-8`; per the other response types, the signature is `new
1617
TextResponse($text, $status = 200, array $headers = []`.
18+
- [#90](https://github.com/zendframework/zend-diactoros/pull/90) adds a new
19+
`Zend\Diactoros\CallbackStream`, allowing you to back a stream with a PHP
20+
callable (such as a generator) to generate the message content. Its
21+
constructor accepts the callable: `$stream = new CallbackStream($callable);`
1722

1823
### Deprecated
1924

src/CallbackStream.php

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<?php
2+
/**
3+
* Zend Framework (http://framework.zend.com/)
4+
*
5+
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
6+
* @copyright Copyright (c) 2015 Oscar Otero (http://oscarotero.com) / Zend Technologies USA Inc. (http://www.zend.com)
7+
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
8+
*/
9+
10+
namespace Zend\Diactoros;
11+
12+
use InvalidArgumentException;
13+
use RuntimeException;
14+
use Psr\Http\Message\StreamInterface;
15+
16+
/**
17+
* Implementation of PSR HTTP streams
18+
*/
19+
class CallbackStream implements StreamInterface
20+
{
21+
/**
22+
* @var callable|null
23+
*/
24+
protected $callback;
25+
26+
/**
27+
* @param callable $callback
28+
* @throws InvalidArgumentException
29+
*/
30+
public function __construct(callable $callback)
31+
{
32+
$this->attach($callback);
33+
}
34+
35+
/**
36+
* {@inheritdoc}
37+
*/
38+
public function __toString()
39+
{
40+
return $this->getContents();
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function close()
47+
{
48+
$this->callback = null;
49+
}
50+
51+
/**
52+
* {@inheritdoc}
53+
*/
54+
public function detach()
55+
{
56+
$callback = $this->callback;
57+
$this->callback = null;
58+
return $callback;
59+
}
60+
61+
/**
62+
* Attach a new callback to the instance.
63+
*
64+
* @param callable $callback
65+
* @throws InvalidArgumentException for callable callback
66+
*/
67+
public function attach(callable $callback)
68+
{
69+
$this->callback = $callback;
70+
}
71+
72+
/**
73+
* {@inheritdoc}
74+
*/
75+
public function getSize()
76+
{
77+
}
78+
79+
/**
80+
* {@inheritdoc}
81+
*/
82+
public function tell()
83+
{
84+
throw new RuntimeException('Callback streams cannot tell position');
85+
}
86+
87+
/**
88+
* {@inheritdoc}
89+
*/
90+
public function eof()
91+
{
92+
return empty($this->callback);
93+
}
94+
95+
/**
96+
* {@inheritdoc}
97+
*/
98+
public function isSeekable()
99+
{
100+
return false;
101+
}
102+
103+
/**
104+
* {@inheritdoc}
105+
*/
106+
public function seek($offset, $whence = SEEK_SET)
107+
{
108+
throw new RuntimeException('Callback streams cannot seek position');
109+
}
110+
111+
/**
112+
* {@inheritdoc}
113+
*/
114+
public function rewind()
115+
{
116+
throw new RuntimeException('Callback streams cannot rewind position');
117+
}
118+
119+
/**
120+
* {@inheritdoc}
121+
*/
122+
public function isWritable()
123+
{
124+
return false;
125+
}
126+
127+
/**
128+
* {@inheritdoc}
129+
*/
130+
public function write($string)
131+
{
132+
throw new RuntimeException('Callback streams cannot write');
133+
}
134+
135+
/**
136+
* {@inheritdoc}
137+
*/
138+
public function isReadable()
139+
{
140+
return false;
141+
}
142+
143+
/**
144+
* {@inheritdoc}
145+
*/
146+
public function read($length)
147+
{
148+
throw new RuntimeException('Callback streams cannot read');
149+
}
150+
151+
/**
152+
* {@inheritdoc}
153+
*/
154+
public function getContents()
155+
{
156+
$callback = $this->detach();
157+
158+
return $callback ? $callback() : '';
159+
}
160+
161+
/**
162+
* {@inheritdoc}
163+
*/
164+
public function getMetadata($key = null)
165+
{
166+
$metadata = [
167+
'eof' => $this->eof(),
168+
'stream_type' => 'callback',
169+
'seekable' => false
170+
];
171+
172+
if (null === $key) {
173+
return $metadata;
174+
}
175+
176+
if (! array_key_exists($key, $metadata)) {
177+
return null;
178+
}
179+
180+
return $metadata[$key];
181+
}
182+
}

src/Response/SapiEmitter.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ public function emit(ResponseInterface $response, $maxBufferLevel = null)
3030
}
3131

3232
if (! $response->hasHeader('Content-Length')) {
33-
$response = $response->withHeader('Content-Length', (string) $response->getBody()->getSize());
33+
// PSR-7 indicates int OR null for the stream size; for null values,
34+
// we will not auto-inject the Content-Length.
35+
if (null !== $response->getBody()->getSize()) {
36+
$response = $response->withHeader('Content-Length', (string) $response->getBody()->getSize());
37+
}
3438
}
3539

3640
$this->emitStatusLine($response);

test/CallbackStreamTest.php

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
<?php
2+
/**
3+
* Zend Framework (http://framework.zend.com/)
4+
*
5+
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
6+
* @copyright Copyright (c) 2015 Oscar Otero (http://oscarotero.com) / Zend Technologies USA Inc. (http://www.zend.com)
7+
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
8+
*/
9+
10+
namespace ZendTest\Diactoros;
11+
12+
use PHPUnit_Framework_TestCase as TestCase;
13+
use Zend\Diactoros\RelativeStream;
14+
use Zend\Diactoros\CallbackStream;
15+
16+
/**
17+
* @covers \Zend\Diactoros\CallbackStream
18+
*/
19+
class CallbackStreamTest extends TestCase
20+
{
21+
public function testToString()
22+
{
23+
$stream = new CallbackStream(function () {
24+
return 'foobarbaz';
25+
});
26+
27+
$ret = $stream->__toString();
28+
$this->assertEquals('foobarbaz', $ret);
29+
}
30+
31+
public function testClose()
32+
{
33+
$stream = new CallbackStream(function () {
34+
});
35+
36+
$stream->close();
37+
38+
$callback = $stream->detach();
39+
40+
$this->assertNull($callback);
41+
}
42+
43+
public function testDetach()
44+
{
45+
$callback = function () {
46+
};
47+
$stream = new CallbackStream($callback);
48+
$ret = $stream->detach();
49+
$this->assertSame($callback, $ret);
50+
}
51+
52+
public function testEof()
53+
{
54+
$stream = new CallbackStream(function () {
55+
});
56+
$ret = $stream->eof();
57+
$this->assertFalse($ret);
58+
59+
$stream->getContents();
60+
$ret = $stream->eof();
61+
$this->assertTrue($ret);
62+
}
63+
64+
public function testGetSize()
65+
{
66+
$stream = new CallbackStream(function () {
67+
});
68+
$ret = $stream->getSize();
69+
$this->assertNull($ret);
70+
}
71+
72+
/**
73+
* @expectedException RuntimeException
74+
*/
75+
public function testTell()
76+
{
77+
$stream = new CallbackStream(function () {
78+
});
79+
$stream->tell();
80+
}
81+
82+
public function testIsSeekable()
83+
{
84+
$stream = new CallbackStream(function () {
85+
});
86+
$ret = $stream->isSeekable();
87+
$this->assertFalse($ret);
88+
}
89+
90+
public function testIsWritable()
91+
{
92+
$stream = new CallbackStream(function () {
93+
});
94+
$ret = $stream->isWritable();
95+
$this->assertFalse($ret);
96+
}
97+
98+
public function testIsReadable()
99+
{
100+
$stream = new CallbackStream(function () {
101+
});
102+
$ret = $stream->isReadable();
103+
$this->assertFalse($ret);
104+
}
105+
106+
/**
107+
* @expectedException RuntimeException
108+
*/
109+
public function testSeek()
110+
{
111+
$stream = new CallbackStream(function () {
112+
});
113+
$stream->seek(0);
114+
}
115+
116+
/**
117+
* @expectedException RuntimeException
118+
*/
119+
public function testRewind()
120+
{
121+
$stream = new CallbackStream(function () {
122+
});
123+
$stream->rewind();
124+
}
125+
126+
/**
127+
* @expectedException RuntimeException
128+
*/
129+
public function testWrite()
130+
{
131+
$stream = new CallbackStream(function () {
132+
});
133+
$stream->write('foobarbaz');
134+
}
135+
136+
/**
137+
* @expectedException RuntimeException
138+
*/
139+
public function testRead()
140+
{
141+
$stream = new CallbackStream(function () {
142+
});
143+
$stream->read(3);
144+
}
145+
146+
public function testGetContents()
147+
{
148+
$stream = new CallbackStream(function () {
149+
return 'foobarbaz';
150+
});
151+
152+
$ret = $stream->getContents();
153+
$this->assertEquals('foobarbaz', $ret);
154+
}
155+
156+
public function testGetMetadata()
157+
{
158+
$stream = new CallbackStream(function () {
159+
});
160+
161+
$ret = $stream->getMetadata('stream_type');
162+
$this->assertEquals('callback', $ret);
163+
164+
$ret = $stream->getMetadata('seekable');
165+
$this->assertFalse($ret);
166+
167+
$ret = $stream->getMetadata('eof');
168+
$this->assertFalse($ret);
169+
}
170+
}

0 commit comments

Comments
 (0)