Skip to content

Commit 67c0c52

Browse files
authored
Add support for PSR17 factories (#38)
* Add support for PSR17 factories * Added changelog * cs * Fixes * Drop older PHP versions that does not support psr/http-factory * Updated phpunit * updated composer deps * Support PHP 7.1 * Allow PHPunit 7.5
1 parent 4a59c1c commit 67c0c52

8 files changed

+127
-31
lines changed

.gitignore

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
.puli/
2-
build/
32
vendor/
43
composer.lock
5-
phpspec.yml
64
phpunit.xml
5+
.phpunit.result.cache

.travis.yml

+4-9
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
language: php
2-
32
sudo: false
43

54
cache:
65
directories:
76
- $HOME/.composer/cache/files
87

98
php:
10-
- 5.5
11-
- 5.6
12-
- 7.0
139
- 7.1
14-
- hhvm
10+
- 7.2
11+
- 7.3
1512

1613
env:
1714
global:
@@ -24,14 +21,12 @@ branches:
2421
matrix:
2522
fast_finish: true
2623
include:
27-
- php: 5.5
24+
- php: 7.3
2825
env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci"
2926

30-
before_install:
31-
- travis_retry composer self-update
3227

3328
install:
34-
- travis_retry composer update ${COMPOSER_FLAGS} --prefer-source --no-interaction
29+
- composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction
3530

3631
script:
3732
- $TEST_COMMAND

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Change Log
22

3+
## UNRELEASED
4+
5+
- Added support for PSR-17 factories.
6+
- Dropped support for PHP < 7.2
7+
38
## 1.0.0 - 2017-05-21
49

510
No changes from 0.2.0.

composer.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@
1111
}
1212
],
1313
"require": {
14-
"php": "^5.5 || ^7.0",
14+
"php": "^7.1",
1515
"psr/http-message": "^1.0",
16+
"psr/http-factory": "^1.0",
1617
"php-http/message-factory": "^1.0.2",
17-
"php-http/discovery": "^1.0"
18+
"php-http/discovery": "^1.7"
1819
},
1920
"require-dev": {
20-
"phpunit/phpunit": "^4.8 || ^5.4",
21+
"phpunit/phpunit": "^7.5 || ^8.3",
2122
"php-http/message": "^1.5",
22-
"zendframework/zend-diactoros": "^1.3.5"
23+
"nyholm/psr7": "^1.0"
2324
},
2425
"autoload": {
2526
"psr-4": {

phpunit.xml.dist

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
convertErrorsToExceptions="true"
66
convertNoticesToExceptions="true"
77
convertWarningsToExceptions="true"
8-
syntaxCheck="true">
8+
>
99
<testsuites>
1010
<testsuite name="MultipartStream tests">
1111
<directory suffix="Test.php">./tests</directory>

src/MultipartStreamBuilder.php

+63-7
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
namespace Http\Message\MultipartStream;
44

5+
use Http\Discovery\Exception\NotFoundException;
6+
use Http\Discovery\Psr17FactoryDiscovery;
57
use Http\Discovery\StreamFactoryDiscovery;
6-
use Http\Message\StreamFactory;
8+
use Http\Message\StreamFactory as HttplugStreamFactory;
9+
use Psr\Http\Message\StreamFactoryInterface;
710
use Psr\Http\Message\StreamInterface;
811

912
/**
@@ -16,7 +19,7 @@
1619
class MultipartStreamBuilder
1720
{
1821
/**
19-
* @var StreamFactory
22+
* @var StreamFactory|StreamFactoryInterface
2023
*/
2124
private $streamFactory;
2225

@@ -36,11 +39,37 @@ class MultipartStreamBuilder
3639
private $data = [];
3740

3841
/**
39-
* @param StreamFactory|null $streamFactory
42+
* @param StreamFactory|StreamFactoryInterface|null $streamFactory
4043
*/
41-
public function __construct(StreamFactory $streamFactory = null)
44+
public function __construct($streamFactory = null)
4245
{
43-
$this->streamFactory = $streamFactory ?: StreamFactoryDiscovery::find();
46+
if ($streamFactory instanceof StreamFactoryInterface || $streamFactory instanceof HttplugStreamFactory) {
47+
$this->streamFactory = $streamFactory;
48+
49+
return;
50+
}
51+
52+
if (null !== $streamFactory) {
53+
throw new \LogicException(sprintf(
54+
'First arguemnt to the constructor of "%s" must be of type "%s", "%s" or null. Got %s',
55+
__CLASS__,
56+
StreamFactoryInterface::class,
57+
HttplugStreamFactory::class,
58+
\is_object($streamFactory) ? \get_class($streamFactory) : \gettype($streamFactory)
59+
));
60+
}
61+
62+
// Try to find a stream factory.
63+
try {
64+
$this->streamFactory = Psr17FactoryDiscovery::findStreamFactory();
65+
} catch (NotFoundException $psr17Exception) {
66+
try {
67+
$this->streamFactory = StreamFactoryDiscovery::find();
68+
} catch (NotFoundException $httplugException) {
69+
// we could not find any factory.
70+
throw $psr17Exception;
71+
}
72+
}
4473
}
4574

4675
/**
@@ -58,7 +87,7 @@ public function __construct(StreamFactory $streamFactory = null)
5887
*/
5988
public function addResource($name, $resource, array $options = [])
6089
{
61-
$stream = $this->streamFactory->createStream($resource);
90+
$stream = $this->createStream($resource);
6291

6392
// validate options['headers'] exists
6493
if (!isset($options['headers'])) {
@@ -108,7 +137,7 @@ public function build()
108137
// Append end
109138
$streams .= "--{$this->getBoundary()}--\r\n";
110139

111-
return $this->streamFactory->createStream($streams);
140+
return $this->createStream($streams);
112141
}
113142

114143
/**
@@ -275,4 +304,31 @@ private function basename($path)
275304

276305
return $filename;
277306
}
307+
308+
/**
309+
* @param string|resource|StreamInterface $resource
310+
*
311+
* @return StreamInterface
312+
*/
313+
private function createStream($resource)
314+
{
315+
if ($resource instanceof StreamInterface) {
316+
return $resource;
317+
}
318+
319+
if ($this->streamFactory instanceof HttplugStreamFactory) {
320+
return $this->streamFactory->createStream($resource);
321+
}
322+
323+
// Assert: We are using a PSR17 stream factory.
324+
if (\is_string($resource)) {
325+
return $this->streamFactory->createStream($resource);
326+
}
327+
328+
if (\is_resource($resource)) {
329+
return $this->streamFactory->createStreamFromResource($resource);
330+
}
331+
332+
throw new \InvalidArgumentException(sprintf('First argument to "%s::createStream()" must be a string, resource or StreamInterface.', __CLASS__));
333+
}
278334
}

tests/CustomMimetypeHelperTest.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
namespace tests\Http\Message\MultipartStream;
44

55
use Http\Message\MultipartStream\CustomMimetypeHelper;
6+
use PHPUnit\Framework\TestCase;
67

7-
class CustomMimetypeHelperTest extends \PHPUnit_Framework_TestCase
8+
class CustomMimetypeHelperTest extends TestCase
89
{
910
public function testGetMimetypeFromExtension()
1011
{

tests/FunctionTest.php

+46-7
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
namespace tests\Http\Message\MultipartStream;
44

5+
use Http\Message\MultipartStream\CustomMimetypeHelper;
56
use Http\Message\MultipartStream\MultipartStreamBuilder;
6-
use Zend\Diactoros\Stream;
7+
use Nyholm\Psr7\Factory\HttplugFactory;
8+
use Nyholm\Psr7\Factory\Psr17Factory;
9+
use Nyholm\Psr7\Stream;
10+
use PHPUnit\Framework\TestCase;
11+
use Psr\Http\Message\StreamInterface;
712

813
/**
914
* @author Tobias Nyholm <[email protected]>
1015
*/
11-
class FunctionTest extends \PHPUnit_Framework_TestCase
16+
class FunctionTest extends TestCase
1217
{
1318
public function testSupportStreams()
1419
{
@@ -46,7 +51,7 @@ public function testSupportURIResources()
4651
$this->assertTrue(false !== strpos($multipartStream, 'Content-Type: image/png'));
4752

4853
$urlContents = file_get_contents($url);
49-
$this->assertContains($urlContents, $multipartStream);
54+
$this->assertStringContainsString($urlContents, $multipartStream);
5055
}
5156

5257
public function testResourceFilenameIsNotLocaleAware()
@@ -139,20 +144,54 @@ public function testReset()
139144

140145
$builder->reset();
141146
$multipartStream = (string) $builder->build();
142-
$this->assertNotContains('foobar', $multipartStream, 'Stream should not have any data after reset()');
147+
$this->assertStringNotContainsString('foobar', $multipartStream, 'Stream should not have any data after reset()');
143148
$this->assertNotEquals($boundary, $builder->getBoundary(), 'Stream should have a new boundary after reset()');
144149
$this->assertNotEmpty($builder->getBoundary());
145150
}
146151

152+
public function testThrowsExceptionIfNotStreamCompatible()
153+
{
154+
$builder = new MultipartStreamBuilder();
155+
$this->expectException(\InvalidArgumentException::class);
156+
$builder->addResource('foo', []);
157+
}
158+
159+
public function testThrowsExceptionInConstructor()
160+
{
161+
$this->expectException(\LogicException::class);
162+
new MultipartStreamBuilder(new CustomMimetypeHelper());
163+
}
164+
165+
/**
166+
* @dataProvider getStreamFactories
167+
*/
168+
public function testSupportDifferentFactories($factory)
169+
{
170+
$resource = fopen(__DIR__.'/Resources/httplug.png', 'r');
171+
172+
$builder = new MultipartStreamBuilder($factory);
173+
$builder->addResource('image', $resource);
174+
175+
$multipartStream = (string) $builder->build();
176+
$this->assertTrue(false !== strpos($multipartStream, 'Content-Disposition: form-data; name="image"; filename="httplug.png"'));
177+
$this->assertTrue(false !== strpos($multipartStream, 'Content-Type: image/png'));
178+
}
179+
180+
public function getStreamFactories()
181+
{
182+
yield 'Httplug Stream Factory' => [new HttplugFactory()];
183+
yield 'PSR-17 Stream Factory' => [new Psr17Factory()];
184+
yield 'Null Stream Factory' => [null];
185+
}
186+
147187
/**
148188
* @param string $body
149189
*
150-
* @return Stream
190+
* @return StreamInterface
151191
*/
152192
private function createStream($body)
153193
{
154-
$stream = new Stream('php://memory', 'rw');
155-
$stream->write($body);
194+
$stream = Stream::create($body);
156195
$stream->rewind();
157196

158197
return $stream;

0 commit comments

Comments
 (0)