Skip to content

Commit 8a81319

Browse files
committed
Add Stream and Response factories with extra resulting objects
1 parent 17cbaa1 commit 8a81319

File tree

9 files changed

+665
-22
lines changed

9 files changed

+665
-22
lines changed

composer.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
"source": "https://github.com/php-fast-forward/http-factory"
1616
},
1717
"require": {
18-
"php": "^8.1",
18+
"php": "^8.2",
1919
"container-interop/service-provider": "^0.4.1",
20-
"fast-forward/container": "^1.2",
20+
"fast-forward/container": "^1.5",
21+
"fast-forward/http-message": "^1.2",
2122
"nyholm/psr7": "^1.8",
2223
"nyholm/psr7-server": "^1.1",
2324
"psr/http-factory": "^1.1",
@@ -48,8 +49,8 @@
4849
}
4950
},
5051
"scripts": {
51-
"cs-check": "php-cs-fixer fix --dry-run --diff",
52-
"cs-fix": "php-cs-fixer fix",
52+
"cs-check": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix --dry-run --diff",
53+
"cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix",
5354
"mutation-testing": "infection --threads=4",
5455
"pre-commit": [
5556
"@cs-check",

src/ResponseFactory.php

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of php-fast-forward/http-factory.
7+
*
8+
* This source file is subject to the license bundled
9+
* with this source code in the file LICENSE.
10+
*
11+
* @link https://github.com/php-fast-forward/http-factory
12+
* @copyright Copyright (c) 2025 Felipe Sayão Lobato Abreu <[email protected]>
13+
* @license https://opensource.org/licenses/MIT MIT License
14+
*/
15+
16+
namespace FastForward\Http\Message\Factory;
17+
18+
use FastForward\Http\Message\EmptyResponse;
19+
use FastForward\Http\Message\HtmlResponse;
20+
use FastForward\Http\Message\JsonResponse;
21+
use FastForward\Http\Message\PayloadResponseInterface;
22+
use FastForward\Http\Message\RedirectResponse;
23+
use FastForward\Http\Message\TextResponse;
24+
use Psr\Http\Message\ResponseFactoryInterface as PsrResponseFactoryInterface;
25+
use Psr\Http\Message\ResponseInterface;
26+
use Psr\Http\Message\UriInterface;
27+
28+
/**
29+
* Class ResponseFactory.
30+
*
31+
* Factory for generating different types of HTTP responses.
32+
* This class encapsulates a PSR-17 response factory and provides
33+
* convenient methods for producing responses with content, payloads, redirects, or no content.
34+
*
35+
* @package FastForward\Http\Message\Factory
36+
*/
37+
final class ResponseFactory implements ResponseFactoryInterface
38+
{
39+
/**
40+
* Constructs the ResponseFactory.
41+
*
42+
* @param PsrResponseFactoryInterface $responseFactory the underlying PSR-17 response factory implementation
43+
*/
44+
public function __construct(
45+
private PsrResponseFactoryInterface $responseFactory,
46+
) {}
47+
48+
/**
49+
* Creates a standard HTTP response.
50+
*
51+
* @param int $code The HTTP status code. Defaults to 200 (OK).
52+
* @param string $reasonPhrase optional reason phrase
53+
*
54+
* @return ResponseInterface the generated response
55+
*/
56+
public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
57+
{
58+
return $this->responseFactory->createResponse($code, $reasonPhrase);
59+
}
60+
61+
/**
62+
* Creates an HTTP response containing HTML content.
63+
*
64+
* The response SHALL have 'Content-Type: text/html' set automatically.
65+
*
66+
* @param string $html the HTML content to include in the response body
67+
*
68+
* @return ResponseInterface the generated HTML response
69+
*/
70+
public function createResponseFromHtml(string $html): ResponseInterface
71+
{
72+
return new HtmlResponse($html);
73+
}
74+
75+
/**
76+
* Creates an HTTP response containing plain text content.
77+
*
78+
* The response SHALL have 'Content-Type: text/plain' set automatically.
79+
*
80+
* @param string $text the plain text content to include in the response body
81+
*
82+
* @return ResponseInterface the generated plain text response
83+
*/
84+
public function createResponseFromText(string $text): ResponseInterface
85+
{
86+
return new TextResponse($text);
87+
}
88+
89+
/**
90+
* Creates an HTTP 204 No Content response.
91+
*
92+
* This response SHALL contain no body and have status code 204.
93+
*
94+
* @param array<string, string|string[]> $headers optional headers to include
95+
*
96+
* @return ResponseInterface the generated no content response
97+
*/
98+
public function createResponseNoContent(array $headers = []): ResponseInterface
99+
{
100+
return new EmptyResponse($headers);
101+
}
102+
103+
/**
104+
* Creates an HTTP response containing a JSON-encoded payload.
105+
*
106+
* The response SHALL have 'Content-Type: application/json' set automatically.
107+
*
108+
* @param array $payload the payload to encode as JSON
109+
*
110+
* @return PayloadResponseInterface the generated JSON response
111+
*/
112+
public function createResponseFromPayload(array $payload): PayloadResponseInterface
113+
{
114+
return new JsonResponse($payload);
115+
}
116+
117+
/**
118+
* Creates an HTTP redirect response.
119+
*
120+
* The response SHALL include a 'Location' header and appropriate status code.
121+
* By default, a temporary (302) redirect is issued unless $permanent is true.
122+
*
123+
* @param string|UriInterface $uri the target location for the redirect
124+
* @param bool $permanent whether to issue a permanent (301) redirect
125+
* @param array<string, string|string[]> $headers optional additional headers
126+
*
127+
* @return ResponseInterface the generated redirect response
128+
*/
129+
public function createResponseRedirect(
130+
string|UriInterface $uri,
131+
bool $permanent = false,
132+
array $headers = [],
133+
): ResponseInterface {
134+
return new RedirectResponse($uri, $permanent, $headers);
135+
}
136+
}

src/ResponseFactoryInterface.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of php-fast-forward/http-factory.
7+
*
8+
* This source file is subject to the license bundled
9+
* with this source code in the file LICENSE.
10+
*
11+
* @link https://github.com/php-fast-forward/http-factory
12+
* @copyright Copyright (c) 2025 Felipe Sayão Lobato Abreu <[email protected]>
13+
* @license https://opensource.org/licenses/MIT MIT License
14+
*/
15+
16+
namespace FastForward\Http\Message\Factory;
17+
18+
use FastForward\Http\Message\PayloadResponseInterface;
19+
use Psr\Http\Message\ResponseFactoryInterface as PsrResponseFactoryInterface;
20+
use Psr\Http\Message\ResponseInterface;
21+
use Psr\Http\Message\UriInterface;
22+
23+
/**
24+
* Interface ResponseFactoryInterface.
25+
*
26+
* Extends PSR-17 ResponseFactoryInterface and defines additional convenience methods
27+
* for generating common types of HTTP responses, including HTML, plain text, JSON payloads,
28+
* redirects, and empty responses.
29+
*
30+
* Implementations of this interface MUST comply with the method requirements and MUST return
31+
* immutable instances as per PSR-7 standards.
32+
*/
33+
interface ResponseFactoryInterface extends PsrResponseFactoryInterface
34+
{
35+
/**
36+
* Creates an HTTP response containing HTML content.
37+
*
38+
* The response MUST have 'Content-Type: text/html' set automatically.
39+
*
40+
* @param string $html the HTML content to include in the response body
41+
*
42+
* @return ResponseInterface the generated HTML response
43+
*/
44+
public function createResponseFromHtml(string $html): ResponseInterface;
45+
46+
/**
47+
* Creates an HTTP response containing a JSON-encoded payload.
48+
*
49+
* The response MUST have 'Content-Type: application/json' set automatically.
50+
*
51+
* @param array $payload the payload to encode as JSON
52+
*
53+
* @return PayloadResponseInterface the generated JSON response
54+
*/
55+
public function createResponseFromPayload(array $payload): PayloadResponseInterface;
56+
57+
/**
58+
* Creates an HTTP response containing plain text content.
59+
*
60+
* The response MUST have 'Content-Type: text/plain' set automatically.
61+
*
62+
* @param string $text the plain text content to include in the response body
63+
*
64+
* @return ResponseInterface the generated plain text response
65+
*/
66+
public function createResponseFromText(string $text): ResponseInterface;
67+
68+
/**
69+
* Creates an HTTP 204 No Content response.
70+
*
71+
* The response MUST contain no body and MUST have status code 204.
72+
*
73+
* @param array<string, string|string[]> $headers optional headers to include in the response
74+
*
75+
* @return ResponseInterface the generated no content response
76+
*/
77+
public function createResponseNoContent(array $headers): ResponseInterface;
78+
79+
/**
80+
* Creates an HTTP redirect response.
81+
*
82+
* The response MUST include a 'Location' header and an appropriate status code:
83+
* 301 (permanent) if $permanent is true, or 302 (temporary) otherwise.
84+
*
85+
* @param string|UriInterface $uri the target location for the redirect
86+
* @param bool $permanent if true, issues a permanent redirect; otherwise, temporary
87+
* @param array<string, string|string[]> $headers optional additional headers to include
88+
*
89+
* @return ResponseInterface the generated redirect response
90+
*/
91+
public function createResponseRedirect(
92+
string|UriInterface $uri,
93+
bool $permanent,
94+
array $headers,
95+
): ResponseInterface;
96+
}

src/ServiceProvider/HttpMessageFactoryServiceProvider.php

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@
1818
use FastForward\Container\Factory\AliasFactory;
1919
use FastForward\Container\Factory\InvokableFactory;
2020
use FastForward\Container\Factory\MethodFactory;
21+
use FastForward\Http\Message\Factory\ResponseFactory;
22+
use FastForward\Http\Message\Factory\ResponseFactoryInterface;
23+
use FastForward\Http\Message\Factory\StreamFactory;
24+
use FastForward\Http\Message\Factory\StreamFactoryInterface;
2125
use Interop\Container\ServiceProviderInterface;
2226
use Nyholm\Psr7\Factory\Psr17Factory;
2327
use Nyholm\Psr7Server\ServerRequestCreator;
28+
use Nyholm\Psr7Server\ServerRequestCreatorInterface;
2429
use Psr\Http\Message\RequestFactoryInterface;
25-
use Psr\Http\Message\ResponseFactoryInterface;
30+
use Psr\Http\Message\ResponseFactoryInterface as PsrResponseFactoryInterface;
2631
use Psr\Http\Message\ServerRequestFactoryInterface;
2732
use Psr\Http\Message\ServerRequestInterface;
28-
use Psr\Http\Message\StreamFactoryInterface;
33+
use Psr\Http\Message\StreamFactoryInterface as PsrStreamFactoryInterface;
2934
use Psr\Http\Message\UploadedFileFactoryInterface;
3035
use Psr\Http\Message\UriFactoryInterface;
3136

@@ -54,24 +59,35 @@ final class HttpMessageFactoryServiceProvider implements ServiceProviderInterfac
5459
public function getFactories(): array
5560
{
5661
return [
57-
Psr17Factory::class => new InvokableFactory(Psr17Factory::class),
58-
ServerRequestCreator::class => new InvokableFactory(
62+
RequestFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
63+
PsrResponseFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
64+
ServerRequestFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
65+
PsrStreamFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
66+
UploadedFileFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
67+
UriFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
68+
ServerRequestCreatorInterface::class => AliasFactory::get(ServerRequestCreator::class),
69+
ResponseFactoryInterface::class => AliasFactory::get(ResponseFactory::class),
70+
StreamFactoryInterface::class => AliasFactory::get(StreamFactory::class),
71+
Psr17Factory::class => new InvokableFactory(Psr17Factory::class),
72+
ServerRequestCreator::class => new InvokableFactory(
5973
ServerRequestCreator::class,
6074
RequestFactoryInterface::class,
6175
UriFactoryInterface::class,
6276
UploadedFileFactoryInterface::class,
6377
StreamFactoryInterface::class,
6478
),
79+
ResponseFactory::class => new InvokableFactory(
80+
ResponseFactory::class,
81+
PsrResponseFactoryInterface::class,
82+
),
83+
StreamFactory::class => new InvokableFactory(
84+
StreamFactory::class,
85+
PsrStreamFactoryInterface::class,
86+
),
6587
ServerRequestInterface::class => new MethodFactory(
6688
ServerRequestCreator::class,
6789
'fromGlobals'
6890
),
69-
RequestFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
70-
ResponseFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
71-
ServerRequestFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
72-
StreamFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
73-
UploadedFileFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
74-
UriFactoryInterface::class => AliasFactory::get(Psr17Factory::class),
7591
];
7692
}
7793

0 commit comments

Comments
 (0)