Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd34e28f

Browse files
committed
support root-level Generator in StreamedJsonResponse
1 parent73a6b4b commitd34e28f

File tree

2 files changed

+78
-42
lines changed

2 files changed

+78
-42
lines changed

‎src/Symfony/Component/HttpFoundation/StreamedJsonResponse.php‎

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ class StreamedJsonResponse extends StreamedResponse
4747
privateconstPLACEHOLDER ='__symfony_json__';
4848

4949
/**
50-
* @param mixed[] $data JSON Data containing PHP generators which will be streamed as list of data
50+
* @param mixed[] $data JSON Data containing PHP generators which will be streamed as list of data or a Generator
5151
* @param int $status The HTTP status code (200 "OK" by default)
5252
* @param array<string, string|string[]> $headers An array of HTTP headers
5353
* @param int $encodingOptions Flags for the json_encode() function
5454
*/
5555
publicfunction__construct(
56-
privatereadonlyarray$data,
56+
privatereadonlyiterable$data,
5757
int$status =200,
5858
array$headers = [],
5959
privateint$encodingOptions = JsonResponse::DEFAULT_ENCODING_OPTIONS,
@@ -66,11 +66,35 @@ public function __construct(
6666
}
6767

6868
privatefunctionstream():void
69+
{
70+
$jsonEncodingOptions = \JSON_THROW_ON_ERROR |$this->encodingOptions;
71+
$keyEncodingOptions =$jsonEncodingOptions & ~\JSON_NUMERIC_CHECK;
72+
73+
$this->streamData($this->data,$jsonEncodingOptions,$keyEncodingOptions);
74+
}
75+
76+
privatefunctionstreamData(mixed$data,int$jsonEncodingOptions,int$keyEncodingOptions):void
77+
{
78+
if (\is_array($data)) {
79+
$this->streamArray($data,$jsonEncodingOptions,$keyEncodingOptions);
80+
81+
return;
82+
}
83+
84+
if (is_iterable($data) && !$datainstanceof \JsonSerializable) {
85+
$this->streamIterable($data,$jsonEncodingOptions,$keyEncodingOptions);
86+
87+
return;
88+
}
89+
90+
echojson_encode($data,$jsonEncodingOptions);
91+
}
92+
93+
privatefunctionstreamArray(array$data,int$jsonEncodingOptions,int$keyEncodingOptions):void
6994
{
7095
$generators = [];
71-
$structure =$this->data;
7296

73-
array_walk_recursive($structure,function (&$item,$key)use (&$generators) {
97+
array_walk_recursive($data,function (&$item,$key)use (&$generators) {
7498
if (self::PLACEHOLDER ===$key) {
7599
// if the placeholder is already in the structure it should be replaced with a new one that explode
76100
// works like expected for the structure
@@ -88,56 +112,51 @@ private function stream(): void
88112
}
89113
});
90114

91-
$jsonEncodingOptions = \JSON_THROW_ON_ERROR |$this->encodingOptions;
92-
$keyEncodingOptions =$jsonEncodingOptions & ~\JSON_NUMERIC_CHECK;
93-
94-
$jsonParts =explode('"'.self::PLACEHOLDER.'"',json_encode($structure,$jsonEncodingOptions));
115+
$jsonParts =explode('"'.self::PLACEHOLDER.'"',json_encode($data,$jsonEncodingOptions));
95116

96117
foreach ($generatorsas$index =>$generator) {
97118
// send first and between parts of the structure
98119
echo$jsonParts[$index];
99120

100-
if ($generatorinstanceof \JsonSerializable || !$generatorinstanceof \Traversable) {
101-
// the placeholders, JsonSerializable and none traversable items in the structure are rendered here
102-
echojson_encode($generator,$jsonEncodingOptions);
103-
104-
continue;
105-
}
121+
$this->streamData($generator,$jsonEncodingOptions,$keyEncodingOptions);
122+
}
106123

107-
$isFirstItem =true;
108-
$startTag ='[';
109-
110-
foreach ($generatoras$key =>$item) {
111-
if ($isFirstItem) {
112-
$isFirstItem =false;
113-
// depending on the first elements key the generator is detected as a list or map
114-
// we can not check for a whole list or map because that would hurt the performance
115-
// of the streamed response which is the main goal of this response class
116-
if (0 !==$key) {
117-
$startTag ='{';
118-
}
119-
120-
echo$startTag;
121-
}else {
122-
// if not first element of the generic, a separator is required between the elements
123-
echo',';
124-
}
124+
// send last part of the structure
125+
echo$jsonParts[array_key_last($jsonParts)];
126+
}
125127

126-
if ('{' ===$startTag) {
127-
echojson_encode((string)$key,$keyEncodingOptions).':';
128+
privatefunctionstreamIterable(iterable$iterable,int$jsonEncodingOptions,int$keyEncodingOptions):void
129+
{
130+
$isFirstItem =true;
131+
$startTag ='[';
132+
133+
foreach ($iterableas$key =>$item) {
134+
if ($isFirstItem) {
135+
$isFirstItem =false;
136+
// depending on the first elements key the generator is detected as a list or map
137+
// we can not check for a whole list or map because that would hurt the performance
138+
// of the streamed response which is the main goal of this response class
139+
if (0 !==$key) {
140+
$startTag ='{';
128141
}
129142

130-
echojson_encode($item,$jsonEncodingOptions);
143+
echo$startTag;
144+
}else {
145+
// if not first element of the generic, a separator is required between the elements
146+
echo',';
131147
}
132148

133-
if ($isFirstItem) {// indicates that the generator was empty
134-
echo'[';
149+
if ('{' ===$startTag) {
150+
echojson_encode((string)$key,$keyEncodingOptions).':';
135151
}
136152

137-
echo'[' ===$startTag ?']' :'}';
153+
$this->streamData($item,$jsonEncodingOptions,$keyEncodingOptions);
138154
}
139155

140-
// send last part of the structure
141-
echo$jsonParts[array_key_last($jsonParts)];
156+
if ($isFirstItem) {// indicates that the generator was empty
157+
echo'[';
158+
}
159+
160+
echo'[' ===$startTag ?']' :'}';
142161
}
143162
}

‎src/Symfony/Component/HttpFoundation/Tests/StreamedJsonResponseTest.php‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ public function testResponseSimpleList()
3030
$this->assertSame('{"_embedded":{"articles":["Article 1","Article 2","Article 3"],"news":["News 1","News 2","News 3"]}}',$content);
3131
}
3232

33+
publicfunctiontestResponseSimpleGenerator()
34+
{
35+
$content =$this->createSendResponse($this->generatorSimple('Article'));
36+
37+
$this->assertSame('["Article 1","Article 2","Article 3"]',$content);
38+
}
39+
40+
publicfunctiontestResponseNestedGenerator()
41+
{
42+
$content =$this->createSendResponse((function ():iterable {
43+
yield'articles' =>$this->generatorSimple('Article');
44+
yield'news' =>$this->generatorSimple('News');
45+
})());
46+
47+
$this->assertSame('{"articles":["Article 1","Article 2","Article 3"],"news":["News 1","News 2","News 3"]}',$content);
48+
}
49+
3350
publicfunctiontestResponseEmptyList()
3451
{
3552
$content =$this->createSendResponse(
@@ -220,9 +237,9 @@ public function testEncodingOptions()
220237
}
221238

222239
/**
223-
* @param mixed[] $data
240+
* @paramiterable<mixed> $data
224241
*/
225-
privatefunctioncreateSendResponse(array$data):string
242+
privatefunctioncreateSendResponse(iterable$data):string
226243
{
227244
$response =newStreamedJsonResponse($data);
228245

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp