class StreamedJsonResponse
StreamedJsonResponse represents a streamed HTTP response for JSON.
A StreamedJsonResponse uses a structure and generics to create an efficient resource-saving JSON response.
It is recommended to use flush() function after a specific number of items to directly stream the data.
@author Alexander Schranz <alexander@sulu.io>
Example usage:
function loadArticles(): \Generator // some streamed loading yield ['title' => 'Article 1']; yield ['title' => 'Article 2']; yield ['title' => 'Article 3']; // recommended to use flush() after every specific number of items }),
$response = new StreamedJsonResponse( // json structure with generators in which will be streamed [ '_embedded' => [ 'articles' => loadArticles(), // any generator which you want to stream as list of data ], ], );
Hierarchy
- class \Symfony\Component\HttpFoundation\Response
- class \Symfony\Component\HttpFoundation\StreamedResponse extends \Symfony\Component\HttpFoundation\Response
- class \Symfony\Component\HttpFoundation\StreamedJsonResponse extends \Symfony\Component\HttpFoundation\StreamedResponse
- class \Symfony\Component\HttpFoundation\StreamedResponse extends \Symfony\Component\HttpFoundation\Response
Expanded class hierarchy of StreamedJsonResponse
See also
flush()
File
-
vendor/
symfony/ http-foundation/ StreamedJsonResponse.php, line 45
Namespace
Symfony\Component\HttpFoundationView source
class StreamedJsonResponse extends StreamedResponse {
private const PLACEHOLDER = '__symfony_json__';
/**
* @param mixed[] $data JSON Data containing PHP generators which will be streamed as list of data or a Generator
* @param int $status The HTTP status code (200 "OK" by default)
* @param array<string, string|string[]> $headers An array of HTTP headers
* @param int $encodingOptions Flags for the json_encode() function
*/
public function __construct(iterable $data, int $status = 200, array $headers = [], int $encodingOptions = JsonResponse::DEFAULT_ENCODING_OPTIONS) {
parent::__construct($this->stream(...), $status, $headers);
if (!$this->headers
->get('Content-Type')) {
$this->headers
->set('Content-Type', 'application/json');
}
}
private function stream() : void {
$jsonEncodingOptions = \JSON_THROW_ON_ERROR | $this->encodingOptions;
$keyEncodingOptions = $jsonEncodingOptions & ~\JSON_NUMERIC_CHECK;
$this->streamData($this->data, $jsonEncodingOptions, $keyEncodingOptions);
}
private function streamData(mixed $data, int $jsonEncodingOptions, int $keyEncodingOptions) : void {
if (\is_array($data)) {
$this->streamArray($data, $jsonEncodingOptions, $keyEncodingOptions);
return;
}
if (is_iterable($data) && !$data instanceof \JsonSerializable) {
$this->streamIterable($data, $jsonEncodingOptions, $keyEncodingOptions);
return;
}
echo json_encode($data, $jsonEncodingOptions);
}
private function streamArray(array $data, int $jsonEncodingOptions, int $keyEncodingOptions) : void {
$generators = [];
array_walk_recursive($data, function (&$item, $key) use (&$generators) {
if (self::PLACEHOLDER === $key) {
// if the placeholder is already in the structure it should be replaced with a new one that explode
// works like expected for the structure
$generators[] = $key;
}
// generators should be used but for better DX all kind of Traversable and objects are supported
if (\is_object($item)) {
$generators[] = $item;
$item = self::PLACEHOLDER;
}
elseif (self::PLACEHOLDER === $item) {
// if the placeholder is already in the structure it should be replaced with a new one that explode
// works like expected for the structure
$generators[] = $item;
}
});
$jsonParts = explode('"' . self::PLACEHOLDER . '"', json_encode($data, $jsonEncodingOptions));
foreach ($generators as $index => $generator) {
// send first and between parts of the structure
echo $jsonParts[$index];
$this->streamData($generator, $jsonEncodingOptions, $keyEncodingOptions);
}
// send last part of the structure
echo $jsonParts[array_key_last($jsonParts)];
}
private function streamIterable(iterable $iterable, int $jsonEncodingOptions, int $keyEncodingOptions) : void {
$isFirstItem = true;
$startTag = '[';
foreach ($iterable as $key => $item) {
if ($isFirstItem) {
$isFirstItem = false;
// depending on the first elements key the generator is detected as a list or map
// we can not check for a whole list or map because that would hurt the performance
// of the streamed response which is the main goal of this response class
if (0 !== $key) {
$startTag = '{';
}
echo $startTag;
}
else {
// if not first element of the generic, a separator is required between the elements
echo ',';
}
if ('{' === $startTag) {
echo json_encode((string) $key, $keyEncodingOptions) . ':';
}
$this->streamData($item, $jsonEncodingOptions, $keyEncodingOptions);
}
if ($isFirstItem) {
// indicates that the generator was empty
echo '[';
}
echo '[' === $startTag ? ']' : '}';
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|
Response::$charset | protected | property | |||
Response::$content | protected | property | |||
Response::$headers | public | property | |||
Response::$sentHeaders | private | property | Tracks headers already sent in informational responses. | ||
Response::$statusCode | protected | property | |||
Response::$statusText | protected | property | |||
Response::$statusTexts | public static | property | Status codes translation table. | ||
Response::$version | protected | property | |||
Response::closeOutputBuffers | public static | function | Cleans or flushes output buffers up to target level. | ||
Response::ensureIEOverSSLCompatibility | protected | function | Checks if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9. | ||
Response::expire | public | function | Marks the response stale by setting the Age header to be equal to the maximum age of the response. | ||
Response::getAge | public | function | Returns the age of the response in seconds. | ||
Response::getCharset | public | function | Retrieves the response charset. | ||
Response::getDate | public | function | Returns the Date header as a DateTime instance. | ||
Response::getEtag | public | function | Returns the literal value of the ETag HTTP header. | ||
Response::getExpires | public | function | Returns the value of the Expires header as a DateTime instance. | ||
Response::getLastModified | public | function | Returns the Last-Modified HTTP header as a DateTime instance. | ||
Response::getMaxAge | public | function | Returns the number of seconds after the time specified in the response's Date header when the response should no longer be considered fresh. |
||
Response::getProtocolVersion | public | function | Gets the HTTP protocol version. | ||
Response::getStatusCode | public | function | Retrieves the status code for the current web response. | ||
Response::getTtl | public | function | Returns the response's time-to-live in seconds. | ||
Response::getVary | public | function | Returns an array of header names given in the Vary header. | ||
Response::hasVary | public | function | Returns true if the response includes a Vary header. | ||
Response::HTTP_ACCEPTED | public | constant | |||
Response::HTTP_ALREADY_REPORTED | public | constant | |||
Response::HTTP_BAD_GATEWAY | public | constant | |||
Response::HTTP_BAD_REQUEST | public | constant | |||
Response::HTTP_CONFLICT | public | constant | |||
Response::HTTP_CONTINUE | public | constant | |||
Response::HTTP_CREATED | public | constant | |||
Response::HTTP_EARLY_HINTS | public | constant | |||
Response::HTTP_EXPECTATION_FAILED | public | constant | |||
Response::HTTP_FAILED_DEPENDENCY | public | constant | |||
Response::HTTP_FORBIDDEN | public | constant | |||
Response::HTTP_FOUND | public | constant | |||
Response::HTTP_GATEWAY_TIMEOUT | public | constant | |||
Response::HTTP_GONE | public | constant | |||
Response::HTTP_IM_USED | public | constant | |||
Response::HTTP_INSUFFICIENT_STORAGE | public | constant | |||
Response::HTTP_INTERNAL_SERVER_ERROR | public | constant | |||
Response::HTTP_I_AM_A_TEAPOT | public | constant | |||
Response::HTTP_LENGTH_REQUIRED | public | constant | |||
Response::HTTP_LOCKED | public | constant | |||
Response::HTTP_LOOP_DETECTED | public | constant | |||
Response::HTTP_METHOD_NOT_ALLOWED | public | constant | |||
Response::HTTP_MISDIRECTED_REQUEST | public | constant | |||
Response::HTTP_MOVED_PERMANENTLY | public | constant | |||
Response::HTTP_MULTIPLE_CHOICES | public | constant | |||
Response::HTTP_MULTI_STATUS | public | constant | |||
Response::HTTP_NETWORK_AUTHENTICATION_REQUIRED | public | constant | |||
Response::HTTP_NON_AUTHORITATIVE_INFORMATION | public | constant | |||
Response::HTTP_NOT_ACCEPTABLE | public | constant | |||
Response::HTTP_NOT_EXTENDED | public | constant | |||
Response::HTTP_NOT_FOUND | public | constant | |||
Response::HTTP_NOT_IMPLEMENTED | public | constant | |||
Response::HTTP_NOT_MODIFIED | public | constant | |||
Response::HTTP_NO_CONTENT | public | constant | |||
Response::HTTP_OK | public | constant | |||
Response::HTTP_PARTIAL_CONTENT | public | constant | |||
Response::HTTP_PAYMENT_REQUIRED | public | constant | |||
Response::HTTP_PERMANENTLY_REDIRECT | public | constant | |||
Response::HTTP_PRECONDITION_FAILED | public | constant | |||
Response::HTTP_PRECONDITION_REQUIRED | public | constant | |||
Response::HTTP_PROCESSING | public | constant | |||
Response::HTTP_PROXY_AUTHENTICATION_REQUIRED | public | constant | |||
Response::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE | public | constant | |||
Response::HTTP_REQUEST_ENTITY_TOO_LARGE | public | constant | |||
Response::HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE | public | constant | |||
Response::HTTP_REQUEST_TIMEOUT | public | constant | |||
Response::HTTP_REQUEST_URI_TOO_LONG | public | constant | |||
Response::HTTP_RESERVED | public | constant | |||
Response::HTTP_RESET_CONTENT | public | constant | |||
Response::HTTP_RESPONSE_CACHE_CONTROL_DIRECTIVES | private | constant | |||
Response::HTTP_SEE_OTHER | public | constant | |||
Response::HTTP_SERVICE_UNAVAILABLE | public | constant | |||
Response::HTTP_SWITCHING_PROTOCOLS | public | constant | |||
Response::HTTP_TEMPORARY_REDIRECT | public | constant | |||
Response::HTTP_TOO_EARLY | public | constant | |||
Response::HTTP_TOO_MANY_REQUESTS | public | constant | |||
Response::HTTP_UNAUTHORIZED | public | constant | |||
Response::HTTP_UNAVAILABLE_FOR_LEGAL_REASONS | public | constant | |||
Response::HTTP_UNPROCESSABLE_ENTITY | public | constant | |||
Response::HTTP_UNSUPPORTED_MEDIA_TYPE | public | constant | |||
Response::HTTP_UPGRADE_REQUIRED | public | constant | |||
Response::HTTP_USE_PROXY | public | constant | |||
Response::HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL | public | constant | |||
Response::HTTP_VERSION_NOT_SUPPORTED | public | constant | |||
Response::isCacheable | public | function | Returns true if the response may safely be kept in a shared (surrogate) cache. | ||
Response::isClientError | public | function | Is there a client error? | ||
Response::isEmpty | public | function | Is the response empty? | ||
Response::isForbidden | public | function | Is the response forbidden? | ||
Response::isFresh | public | function | Returns true if the response is "fresh". | ||
Response::isImmutable | public | function | Returns true if the response is marked as "immutable". | ||
Response::isInformational | public | function | Is response informative? | ||
Response::isInvalid | public | function | Is response invalid? | ||
Response::isNotFound | public | function | Is the response a not found error? | ||
Response::isNotModified | public | function | Determines if the Response validators (ETag, Last-Modified) match a conditional value specified in the Request. |
||
Response::isOk | public | function | Is the response OK? | ||
Response::isRedirect | public | function | Is the response a redirect of some form? | ||
Response::isRedirection | public | function | Is the response a redirect? | ||
Response::isServerError | public | function | Was there a server side error? | ||
Response::isSuccessful | public | function | Is response successful? | ||
Response::isValidateable | public | function | Returns true if the response includes headers that can be used to validate the response with the origin server using a conditional GET request. |
||
Response::mustRevalidate | public | function | Returns true if the response must be revalidated by shared caches once it has become stale. | ||
Response::prepare | public | function | Prepares the Response before it is sent to the client. | 1 | |
Response::send | public | function | Sends HTTP headers and content. | ||
Response::setCache | public | function | Sets the response's cache headers (validation and/or expiration). | ||
Response::setCharset | public | function | Sets the response charset. | ||
Response::setClientTtl | public | function | Sets the response's time-to-live for private/client caches in seconds. | ||
Response::setContentSafe | public | function | Marks a response as safe according to RFC8674. | ||
Response::setDate | public | function | Sets the Date header. | ||
Response::setEtag | public | function | Sets the ETag value. | ||
Response::setExpires | public | function | Sets the Expires HTTP header with a DateTime instance. | ||
Response::setImmutable | public | function | Marks the response as "immutable". | ||
Response::setLastModified | public | function | Sets the Last-Modified HTTP header with a DateTime instance. | ||
Response::setMaxAge | public | function | Sets the number of seconds after which the response should no longer be considered fresh. | ||
Response::setNotModified | public | function | Modifies the response so that it conforms to the rules defined for a 304 status code. | ||
Response::setPrivate | public | function | Marks the response as "private". | ||
Response::setProtocolVersion | public | function | Sets the HTTP protocol version (1.0 or 1.1). | ||
Response::setPublic | public | function | Marks the response as "public". | ||
Response::setSharedMaxAge | public | function | Sets the number of seconds after which the response should no longer be considered fresh by shared caches. | ||
Response::setStaleIfError | public | function | Sets the number of seconds after which the response should no longer be returned by shared caches when backend is down. | ||
Response::setStaleWhileRevalidate | public | function | Sets the number of seconds after which the response should no longer return stale content by shared caches. | ||
Response::setStatusCode | public | function | Sets the response status code. | ||
Response::setTtl | public | function | Sets the response's time-to-live for shared caches in seconds. | ||
Response::setVary | public | function | Sets the Vary header. | ||
Response::__clone | public | function | Clones the current Response instance. | ||
Response::__toString | public | function | Returns the Response as an HTTP string. | ||
StreamedJsonResponse::PLACEHOLDER | private | constant | |||
StreamedJsonResponse::stream | private | function | |||
StreamedJsonResponse::streamArray | private | function | |||
StreamedJsonResponse::streamData | private | function | |||
StreamedJsonResponse::streamIterable | private | function | |||
StreamedJsonResponse::__construct | public | function | Overrides StreamedResponse::__construct | ||
StreamedResponse::$callback | protected | property | |||
StreamedResponse::$headersSent | private | property | |||
StreamedResponse::$streamed | protected | property | |||
StreamedResponse::getCallback | public | function | |||
StreamedResponse::getContent | public | function | Gets the current response content. | Overrides Response::getContent | |
StreamedResponse::sendContent | public | function | This method only sends the content once. | Overrides Response::sendContent | |
StreamedResponse::sendHeaders | public | function | This method only sends the headers once. | Overrides Response::sendHeaders | |
StreamedResponse::setCallback | public | function | Sets the PHP callback associated with this Response. | ||
StreamedResponse::setContent | public | function | Overrides Response::setContent |