Skip to content

Commit 3a4caff

Browse files
authored
Replace data response by data stream (big refactoring) (#107)
1 parent 9f69c37 commit 3a4caff

File tree

68 files changed

+3446
-69
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+3446
-69
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ name: build
2626
jobs:
2727
phpunit:
2828
uses: yiisoft/actions/.github/workflows/phpunit.yml@master
29+
secrets:
30+
codecovToken: ${{ secrets.CODECOV_TOKEN }}
2931
with:
3032
os: >-
3133
['ubuntu-latest', 'windows-latest']

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33
## 2.1.3 under development
44

5+
- New #107: Add `DataStream` (@vjik)
6+
- New #107: Add `FormatterInterface` and implementations: `HtmlFormatter`, `JsonFormatter`, `PlainTextFormatter`,
7+
`XmlFormatter` (@vjik)
8+
- New #107: Add `DataResponseFactoryInterface` and implementations: `DataResponseFactory`, `FormattedResponseFactory`,
9+
`HtmlResponseFactory`, `JsonResponseFactory`, `PlainTextResponseFactory`, `XmlResponseFactory` (@vjik)
10+
- New #107: Add middlewares: `XmlDataResponseMiddleware`, `HtmlDataResponseMiddleware`, `JsonDataResponseMiddleware`,
11+
`PlainTextDataResponseMiddleware` and `DataResponseMiddleware` (@vjik)
12+
- New #107: Add `ContentNegotiatorResponseFactory` and `ContentNegotiatorDataResponseMiddleware` (@vjik)
13+
- Chg #107: Deprecate `DataResponse`, `DataResponseFactory`, `DataResponseFactoryInterface`,
14+
`DataResponseFormatterInterface`, `ResponseContentTrait`, `HtmlDataResponseFormatter`,
15+
`JsonDataResponseFormatter`, `PlainTextDataResponseFormatter`, `XmlDataResponseFormatter`, `ContentNegotiator`,
16+
`FormatDataResponse`, `FormatDataResponseAsHtml`, `FormatDataResponseAsJson`, `FormatDataResponseAsPlainText`,
17+
`FormatDataResponseAsXml` (@vjik)
518
- Enh #106: Explicitly import classes, functions, and constants in "use" section (@mspirkov)
619
- Enh #108: Remove unnecessary files from Composer package (@mspirkov)
720

README.md

Lines changed: 93 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -32,99 +32,133 @@ composer require yiisoft/data-response
3232

3333
## General usage
3434

35-
The package provides `DataResponseFactory` class that, given a [PSR-17](https://www.php-fig.org/psr/psr-17/)
36-
response factory, is able to create data response.
35+
### Response Factories
3736

38-
Data response contains raw data to be processed later.
37+
The package provides response factories that create [PSR-7](https://www.php-fig.org/psr/psr-7/) responses
38+
with `DataStream` body. The data is formatted lazily when the response body is read.
3939

4040
```php
41-
use Yiisoft\DataResponse\DataResponseFactory;
41+
use Yiisoft\DataResponse\ResponseFactory\JsonResponseFactory;
42+
use Yiisoft\DataResponse\Formatter\JsonFormatter;
4243

4344
/**
4445
* @var Psr\Http\Message\ResponseFactoryInterface $responseFactory
4546
*/
4647

47-
$factory = new DataResponseFactory($responseFactory);
48-
$dataResponse = $factory->createResponse('test');
49-
$dataResponse
50-
->getBody()
51-
->rewind();
48+
$factory = new JsonResponseFactory($responseFactory, new JsonFormatter());
49+
$response = $factory->createResponse(['key' => 'value']);
5250

53-
echo $dataResponse
54-
->getBody()
55-
->getContents(); // "test"
51+
$response->getBody()->rewind();
52+
echo $response->getBody()->getContents(); // {"key":"value"}
53+
echo $response->getHeaderLine('Content-Type'); // application/json; charset=UTF-8
5654
```
5755

58-
### Formatters
56+
The following response factories are available:
5957

60-
Formatter purpose is to format a data response. In the following example we format data as JSON.
58+
- `JsonResponseFactory` — creates responses with JSON-formatted body;
59+
- `XmlResponseFactory` — creates responses with XML-formatted body;
60+
- `HtmlResponseFactory` — creates responses with HTML-formatted body;
61+
- `PlainTextResponseFactory` — creates responses with plain text body;
62+
- `DataResponseFactory` — creates responses without a predefined formatter, use middleware to format.
6163

62-
```php
63-
use Yiisoft\DataResponse\DataResponseFactory;
64-
use Yiisoft\DataResponse\Formatter\JsonDataResponseFormatter;
64+
### Middleware
6565

66-
/**
67-
* @var Psr\Http\Message\ResponseFactoryInterface $responseFactory
68-
*/
66+
The package provides [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware that formats `DataStream` responses
67+
without a predefined formatter.
68+
69+
```php
70+
use Yiisoft\DataResponse\Middleware\JsonDataResponseMiddleware;
71+
use Yiisoft\DataResponse\Formatter\JsonFormatter;
6972

70-
$factory = new DataResponseFactory($responseFactory);
71-
$dataResponse = $factory->createResponse('test');
72-
$dataResponse = $dataResponse->withResponseFormatter(new JsonDataResponseFormatter());
73-
$dataResponse
74-
->getBody()
75-
->rewind();
76-
77-
echo $dataResponse->getHeader('Content-Type'); // ["application/json; charset=UTF-8"]
78-
echo $dataResponse
79-
->getBody()
80-
->getContents(); // "test"
73+
$middleware = new JsonDataResponseMiddleware(new JsonFormatter());
8174
```
8275

83-
The following formatters are available:
76+
The following middleware are available:
8477

85-
- `HtmlDataResponseFormatter`
86-
- `JsonDataResponseFormatter`
87-
- `XmlDataResponseFormatter`
88-
- `PlainTextDataResponseFormatter`
78+
- `HtmlDataResponseMiddleware`
79+
- `JsonDataResponseMiddleware`
80+
- `XmlDataResponseMiddleware`
81+
- `PlainTextDataResponseMiddleware`
8982

90-
### Middleware
83+
### Content Negotiation
9184

92-
The package provides a [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware that is able to format a data response.
85+
The package provides content negotiation via middleware and response factory.
9386

94-
```php
95-
use Yiisoft\DataResponse\Middleware\FormatDataResponse;
96-
use Yiisoft\DataResponse\Formatter\JsonDataResponseFormatter;
87+
#### Middleware
9788

98-
$middleware = (new FormatDataResponse(new JsonDataResponseFormatter()));
99-
//$middleware->process($request, $handler);
89+
`ContentNegotiatorDataResponseMiddleware` selects a formatter based on the request's `Accept` header:
90+
91+
```php
92+
use Yiisoft\DataResponse\Formatter\HtmlFormatter;
93+
use Yiisoft\DataResponse\Formatter\XmlFormatter;
94+
use Yiisoft\DataResponse\Formatter\JsonFormatter;
95+
use Yiisoft\DataResponse\Middleware\ContentNegotiatorDataResponseMiddleware;
96+
97+
$middleware = new ContentNegotiatorDataResponseMiddleware(
98+
formatters: [
99+
'text/html' => new HtmlFormatter(),
100+
'application/xml' => new XmlFormatter(),
101+
'application/json' => new JsonFormatter(),
102+
],
103+
fallback: new JsonFormatter(),
104+
);
100105
```
101106

102-
Also, the package provides [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware for content negotiation:
107+
The `fallback` parameter also accepts a `RequestHandlerInterface`, for example `NotAcceptableRequestHandler`
108+
to return a 406 response when no formatter matches.
109+
110+
#### Response Factory
111+
112+
`ContentNegotiatorResponseFactory` selects a response factory based on the request's `Accept` header:
103113

104114
```php
105-
use Yiisoft\DataResponse\Formatter\HtmlDataResponseFormatter;
106-
use Yiisoft\DataResponse\Formatter\XmlDataResponseFormatter;
107-
use Yiisoft\DataResponse\Formatter\JsonDataResponseFormatter;
108-
use Yiisoft\DataResponse\Middleware\ContentNegotiator;
109-
110-
$middleware = new ContentNegotiator([
111-
'text/html' => new HtmlDataResponseFormatter(),
112-
'application/xml' => new XmlDataResponseFormatter(),
113-
'application/json' => new JsonDataResponseFormatter(),
114-
]);
115+
use Yiisoft\DataResponse\ResponseFactory\ContentNegotiatorResponseFactory;
116+
use Yiisoft\DataResponse\ResponseFactory\JsonResponseFactory;
117+
use Yiisoft\DataResponse\ResponseFactory\XmlResponseFactory;
118+
119+
/**
120+
* @var JsonResponseFactory $jsonResponseFactory
121+
* @var XmlResponseFactory $xmlResponseFactory
122+
*/
123+
124+
$factory = new ContentNegotiatorResponseFactory(
125+
factories: [
126+
'application/json' => $jsonResponseFactory,
127+
'application/xml' => $xmlResponseFactory,
128+
],
129+
fallback: $jsonResponseFactory,
130+
);
131+
132+
$response = $factory->createResponse($request, ['key' => 'value']);
115133
```
116134

117-
You can override middlewares with method `withContentFormatters()`:
135+
The `fallback` parameter also accepts a `RequestHandlerInterface`, for example `NotAcceptableRequestHandler`
136+
to return a 406 response when no factory matches.
137+
138+
### DataStream
139+
140+
`DataStream` is a [PSR-7](https://www.php-fig.org/psr/psr-7/) stream that lazily formats data.
141+
It wraps raw data and a formatter, and performs formatting only when the stream is read.
142+
A formatter is optional at construction time, but must be set before reading the stream,
143+
otherwise a `LogicException` will be thrown.
118144

119145
```php
120-
$middleware->withContentFormatters([
121-
'application/xml' => new XmlDataResponseFormatter(),
122-
'application/json' => new JsonDataResponseFormatter(),
123-
]);
146+
use Yiisoft\DataResponse\DataStream\DataStream;
147+
use Yiisoft\DataResponse\Formatter\JsonFormatter;
148+
use Yiisoft\DataResponse\Formatter\XmlFormatter;
149+
150+
$stream = new DataStream(['key' => 'value'], new JsonFormatter());
151+
152+
echo (string) $stream; // {"key":"value"}
153+
154+
// You can change the data or formatter dynamically
155+
$stream->changeData(['new' => 'data']);
156+
$stream->changeFormatter(new XmlFormatter());
124157
```
125158

126159
## Documentation
127160

161+
- [Deprecated classes](docs/deprecated.md)
128162
- [Internals](docs/internals.md)
129163

130164
If you need help or have a question, the [Yii Forum](https://forum.yiiframework.com/c/yii-3-0/63) is a good place for that.

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
"rector/rector": "^2.1.4",
4747
"roave/infection-static-analysis-plugin": "^1.35",
4848
"vimeo/psalm": "^5.26.1 || ^6.8.0",
49-
"yiisoft/di": "^1.4"
49+
"yiisoft/di": "^1.4",
50+
"yiisoft/test-support": "^3.2"
5051
},
5152
"autoload": {
5253
"psr-4": {
@@ -82,6 +83,6 @@
8283
},
8384
"scripts": {
8485
"require-checker": "composer-require-checker",
85-
"test": "phpunit --testdox --no-interaction"
86+
"test": "phpunit --testdox"
8687
}
8788
}

config/di-web.php

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,55 @@
22

33
declare(strict_types=1);
44

5-
use Yiisoft\DataResponse\DataResponseFactory;
6-
use Yiisoft\DataResponse\DataResponseFactoryInterface;
5+
use Yiisoft\DataResponse\DataResponseFactory as DeprecatedDataResponseFactory;
6+
use Yiisoft\DataResponse\DataResponseFactoryInterface as DeprecatedDataResponseFactoryInterface;
77
use Yiisoft\DataResponse\DataResponseFormatterInterface;
88
use Yiisoft\DataResponse\Formatter\HtmlDataResponseFormatter;
9+
use Yiisoft\DataResponse\Formatter\HtmlFormatter;
10+
use Yiisoft\DataResponse\Formatter\JsonFormatter;
11+
use Yiisoft\DataResponse\Formatter\XmlFormatter;
912
use Yiisoft\DataResponse\Middleware\ContentNegotiator;
13+
use Yiisoft\DataResponse\Middleware\ContentNegotiatorDataResponseMiddleware;
14+
use Yiisoft\DataResponse\NotAcceptableRequestHandler;
15+
use Yiisoft\DataResponse\ResponseFactory\ContentNegotiatorResponseFactory;
16+
use Yiisoft\DataResponse\ResponseFactory\DataResponseFactory;
17+
use Yiisoft\DataResponse\ResponseFactory\DataResponseFactoryInterface;
18+
use Yiisoft\DataResponse\ResponseFactory\HtmlResponseFactory;
19+
use Yiisoft\DataResponse\ResponseFactory\JsonResponseFactory;
20+
use Yiisoft\DataResponse\ResponseFactory\XmlResponseFactory;
1021
use Yiisoft\Definitions\DynamicReferencesArray;
22+
use Yiisoft\Definitions\Reference;
23+
use Yiisoft\Definitions\ReferencesArray;
1124

1225
/* @var $params array */
1326

1427
return [
1528
DataResponseFormatterInterface::class => HtmlDataResponseFormatter::class,
16-
DataResponseFactoryInterface::class => DataResponseFactory::class,
29+
DeprecatedDataResponseFactoryInterface::class => DeprecatedDataResponseFactory::class,
1730
ContentNegotiator::class => [
1831
'__construct()' => [
1932
'contentFormatters' => DynamicReferencesArray::from($params['yiisoft/data-response']['contentFormatters']),
2033
],
2134
],
35+
DataResponseFactoryInterface::class => DataResponseFactory::class,
36+
ContentNegotiatorDataResponseMiddleware::class => [
37+
'__construct()' => [
38+
'formatters' => ReferencesArray::from([
39+
'text/html' => HtmlFormatter::class,
40+
'application/xml' => XmlFormatter::class,
41+
'application/json' => JsonFormatter::class,
42+
]),
43+
'fallback' => Reference::to(NotAcceptableRequestHandler::class),
44+
],
45+
],
46+
ContentNegotiatorResponseFactory::class => [
47+
'__construct()' => [
48+
'factories' => DynamicReferencesArray::from([
49+
'text/html' => HtmlResponseFactory::class,
50+
'application/xml' => XmlResponseFactory::class,
51+
'application/json' => JsonResponseFactory::class,
52+
]),
53+
'fallback' => Reference::to(NotAcceptableRequestHandler::class),
54+
],
55+
],
2256
];

docs/deprecated.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Deprecated
2+
3+
> [!WARNING]
4+
> The classes described in this document are deprecated and will be removed in a future version.
5+
6+
## General usage
7+
8+
The package provides `DataResponseFactory` class that, given a [PSR-17](https://www.php-fig.org/psr/psr-17/)
9+
response factory, is able to create data response.
10+
11+
Data response contains raw data to be processed later.
12+
13+
```php
14+
use Yiisoft\DataResponse\DataResponseFactory;
15+
16+
/**
17+
* @var Psr\Http\Message\ResponseFactoryInterface $responseFactory
18+
*/
19+
20+
$factory = new DataResponseFactory($responseFactory);
21+
$dataResponse = $factory->createResponse('test');
22+
$dataResponse
23+
->getBody()
24+
->rewind();
25+
26+
echo $dataResponse
27+
->getBody()
28+
->getContents(); // "test"
29+
```
30+
31+
## Formatters
32+
33+
Formatter purpose is to format a data response. In the following example we format data as JSON.
34+
35+
```php
36+
use Yiisoft\DataResponse\DataResponseFactory;
37+
use Yiisoft\DataResponse\Formatter\JsonDataResponseFormatter;
38+
39+
/**
40+
* @var Psr\Http\Message\ResponseFactoryInterface $responseFactory
41+
*/
42+
43+
$factory = new DataResponseFactory($responseFactory);
44+
$dataResponse = $factory->createResponse('test');
45+
$dataResponse = $dataResponse->withResponseFormatter(new JsonDataResponseFormatter());
46+
$dataResponse
47+
->getBody()
48+
->rewind();
49+
50+
echo $dataResponse->getHeader('Content-Type'); // ["application/json; charset=UTF-8"]
51+
echo $dataResponse
52+
->getBody()
53+
->getContents(); // "test"
54+
```
55+
56+
The following formatters are available:
57+
58+
- `HtmlDataResponseFormatter`
59+
- `JsonDataResponseFormatter`
60+
- `XmlDataResponseFormatter`
61+
- `PlainTextDataResponseFormatter`
62+
63+
## Middleware
64+
65+
The package provides a [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware that is able to format a data response.
66+
67+
```php
68+
use Yiisoft\DataResponse\Middleware\FormatDataResponse;
69+
use Yiisoft\DataResponse\Formatter\JsonDataResponseFormatter;
70+
71+
$middleware = (new FormatDataResponse(new JsonDataResponseFormatter()));
72+
//$middleware->process($request, $handler);
73+
```
74+
75+
Also, the package provides [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware for content negotiation:
76+
77+
```php
78+
use Yiisoft\DataResponse\Formatter\HtmlDataResponseFormatter;
79+
use Yiisoft\DataResponse\Formatter\XmlDataResponseFormatter;
80+
use Yiisoft\DataResponse\Formatter\JsonDataResponseFormatter;
81+
use Yiisoft\DataResponse\Middleware\ContentNegotiator;
82+
83+
$middleware = new ContentNegotiator([
84+
'text/html' => new HtmlDataResponseFormatter(),
85+
'application/xml' => new XmlDataResponseFormatter(),
86+
'application/json' => new JsonDataResponseFormatter(),
87+
]);
88+
```
89+
90+
You can override middlewares with method `withContentFormatters()`:
91+
92+
```php
93+
$middleware->withContentFormatters([
94+
'application/xml' => new XmlDataResponseFormatter(),
95+
'application/json' => new JsonDataResponseFormatter(),
96+
]);
97+
```

0 commit comments

Comments
 (0)