Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@12
|
3 * @see https://github.com/zendframework/zend-diactoros for the canonical source repository
|
Chris@12
|
4 * @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
|
Chris@0
|
5 * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
Chris@0
|
6 */
|
Chris@0
|
7
|
Chris@0
|
8 namespace Zend\Diactoros\Response;
|
Chris@0
|
9
|
Chris@0
|
10 use InvalidArgumentException;
|
Chris@0
|
11 use Zend\Diactoros\Response;
|
Chris@0
|
12 use Zend\Diactoros\Stream;
|
Chris@0
|
13
|
Chris@16
|
14 use function is_object;
|
Chris@16
|
15 use function is_resource;
|
Chris@16
|
16 use function json_encode;
|
Chris@16
|
17 use function json_last_error;
|
Chris@16
|
18 use function json_last_error_msg;
|
Chris@16
|
19 use function sprintf;
|
Chris@16
|
20
|
Chris@16
|
21 use const JSON_ERROR_NONE;
|
Chris@16
|
22
|
Chris@0
|
23 /**
|
Chris@0
|
24 * JSON response.
|
Chris@0
|
25 *
|
Chris@0
|
26 * Allows creating a response by passing data to the constructor; by default,
|
Chris@0
|
27 * serializes the data to JSON, sets a status code of 200 and sets the
|
Chris@0
|
28 * Content-Type header to application/json.
|
Chris@0
|
29 */
|
Chris@0
|
30 class JsonResponse extends Response
|
Chris@0
|
31 {
|
Chris@0
|
32 use InjectContentTypeTrait;
|
Chris@0
|
33
|
Chris@0
|
34 /**
|
Chris@0
|
35 * Default flags for json_encode; value of:
|
Chris@0
|
36 *
|
Chris@0
|
37 * <code>
|
Chris@0
|
38 * JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES
|
Chris@0
|
39 * </code>
|
Chris@0
|
40 *
|
Chris@0
|
41 * @const int
|
Chris@0
|
42 */
|
Chris@0
|
43 const DEFAULT_JSON_FLAGS = 79;
|
Chris@0
|
44
|
Chris@0
|
45 /**
|
Chris@12
|
46 * @var mixed
|
Chris@12
|
47 */
|
Chris@12
|
48 private $payload;
|
Chris@12
|
49
|
Chris@12
|
50 /**
|
Chris@12
|
51 * @var int
|
Chris@12
|
52 */
|
Chris@12
|
53 private $encodingOptions;
|
Chris@12
|
54
|
Chris@12
|
55 /**
|
Chris@0
|
56 * Create a JSON response with the given data.
|
Chris@0
|
57 *
|
Chris@0
|
58 * Default JSON encoding is performed with the following options, which
|
Chris@0
|
59 * produces RFC4627-compliant JSON, capable of embedding into HTML.
|
Chris@0
|
60 *
|
Chris@0
|
61 * - JSON_HEX_TAG
|
Chris@0
|
62 * - JSON_HEX_APOS
|
Chris@0
|
63 * - JSON_HEX_AMP
|
Chris@0
|
64 * - JSON_HEX_QUOT
|
Chris@0
|
65 * - JSON_UNESCAPED_SLASHES
|
Chris@0
|
66 *
|
Chris@0
|
67 * @param mixed $data Data to convert to JSON.
|
Chris@0
|
68 * @param int $status Integer status code for the response; 200 by default.
|
Chris@0
|
69 * @param array $headers Array of headers to use at initialization.
|
Chris@0
|
70 * @param int $encodingOptions JSON encoding options to use.
|
Chris@0
|
71 * @throws InvalidArgumentException if unable to encode the $data to JSON.
|
Chris@0
|
72 */
|
Chris@0
|
73 public function __construct(
|
Chris@0
|
74 $data,
|
Chris@0
|
75 $status = 200,
|
Chris@0
|
76 array $headers = [],
|
Chris@0
|
77 $encodingOptions = self::DEFAULT_JSON_FLAGS
|
Chris@0
|
78 ) {
|
Chris@12
|
79 $this->setPayload($data);
|
Chris@12
|
80 $this->encodingOptions = $encodingOptions;
|
Chris@12
|
81
|
Chris@12
|
82 $json = $this->jsonEncode($data, $this->encodingOptions);
|
Chris@12
|
83 $body = $this->createBodyFromJson($json);
|
Chris@0
|
84
|
Chris@0
|
85 $headers = $this->injectContentType('application/json', $headers);
|
Chris@0
|
86
|
Chris@0
|
87 parent::__construct($body, $status, $headers);
|
Chris@0
|
88 }
|
Chris@0
|
89
|
Chris@0
|
90 /**
|
Chris@12
|
91 * @return mixed
|
Chris@12
|
92 */
|
Chris@12
|
93 public function getPayload()
|
Chris@12
|
94 {
|
Chris@12
|
95 return $this->payload;
|
Chris@12
|
96 }
|
Chris@12
|
97
|
Chris@12
|
98 /**
|
Chris@12
|
99 * @param $data
|
Chris@12
|
100 *
|
Chris@12
|
101 * @return JsonResponse
|
Chris@12
|
102 */
|
Chris@12
|
103 public function withPayload($data)
|
Chris@12
|
104 {
|
Chris@12
|
105 $new = clone $this;
|
Chris@12
|
106 $new->setPayload($data);
|
Chris@12
|
107 return $this->updateBodyFor($new);
|
Chris@12
|
108 }
|
Chris@12
|
109
|
Chris@12
|
110 /**
|
Chris@12
|
111 * @return int
|
Chris@12
|
112 */
|
Chris@12
|
113 public function getEncodingOptions()
|
Chris@12
|
114 {
|
Chris@12
|
115 return $this->encodingOptions;
|
Chris@12
|
116 }
|
Chris@12
|
117
|
Chris@12
|
118 /**
|
Chris@12
|
119 * @param int $encodingOptions
|
Chris@12
|
120 *
|
Chris@12
|
121 * @return JsonResponse
|
Chris@12
|
122 */
|
Chris@12
|
123 public function withEncodingOptions($encodingOptions)
|
Chris@12
|
124 {
|
Chris@12
|
125 $new = clone $this;
|
Chris@12
|
126 $new->encodingOptions = $encodingOptions;
|
Chris@12
|
127 return $this->updateBodyFor($new);
|
Chris@12
|
128 }
|
Chris@12
|
129
|
Chris@12
|
130 /**
|
Chris@12
|
131 * @param string $json
|
Chris@12
|
132 *
|
Chris@12
|
133 * @return Stream
|
Chris@12
|
134 */
|
Chris@12
|
135 private function createBodyFromJson($json)
|
Chris@12
|
136 {
|
Chris@12
|
137 $body = new Stream('php://temp', 'wb+');
|
Chris@12
|
138 $body->write($json);
|
Chris@12
|
139 $body->rewind();
|
Chris@12
|
140
|
Chris@12
|
141 return $body;
|
Chris@12
|
142 }
|
Chris@12
|
143
|
Chris@12
|
144 /**
|
Chris@0
|
145 * Encode the provided data to JSON.
|
Chris@0
|
146 *
|
Chris@0
|
147 * @param mixed $data
|
Chris@0
|
148 * @param int $encodingOptions
|
Chris@0
|
149 * @return string
|
Chris@0
|
150 * @throws InvalidArgumentException if unable to encode the $data to JSON.
|
Chris@0
|
151 */
|
Chris@0
|
152 private function jsonEncode($data, $encodingOptions)
|
Chris@0
|
153 {
|
Chris@0
|
154 if (is_resource($data)) {
|
Chris@0
|
155 throw new InvalidArgumentException('Cannot JSON encode resources');
|
Chris@0
|
156 }
|
Chris@0
|
157
|
Chris@0
|
158 // Clear json_last_error()
|
Chris@0
|
159 json_encode(null);
|
Chris@0
|
160
|
Chris@0
|
161 $json = json_encode($data, $encodingOptions);
|
Chris@0
|
162
|
Chris@0
|
163 if (JSON_ERROR_NONE !== json_last_error()) {
|
Chris@0
|
164 throw new InvalidArgumentException(sprintf(
|
Chris@0
|
165 'Unable to encode data to JSON in %s: %s',
|
Chris@0
|
166 __CLASS__,
|
Chris@0
|
167 json_last_error_msg()
|
Chris@0
|
168 ));
|
Chris@0
|
169 }
|
Chris@0
|
170
|
Chris@0
|
171 return $json;
|
Chris@0
|
172 }
|
Chris@12
|
173
|
Chris@12
|
174 /**
|
Chris@12
|
175 * @param $data
|
Chris@12
|
176 */
|
Chris@12
|
177 private function setPayload($data)
|
Chris@12
|
178 {
|
Chris@12
|
179 if (is_object($data)) {
|
Chris@12
|
180 $data = clone $data;
|
Chris@12
|
181 }
|
Chris@12
|
182
|
Chris@12
|
183 $this->payload = $data;
|
Chris@12
|
184 }
|
Chris@12
|
185
|
Chris@12
|
186 /**
|
Chris@12
|
187 * Update the response body for the given instance.
|
Chris@12
|
188 *
|
Chris@12
|
189 * @param self $toUpdate Instance to update.
|
Chris@12
|
190 * @return JsonResponse Returns a new instance with an updated body.
|
Chris@12
|
191 */
|
Chris@12
|
192 private function updateBodyFor(self $toUpdate)
|
Chris@12
|
193 {
|
Chris@12
|
194 $json = $this->jsonEncode($toUpdate->payload, $toUpdate->encodingOptions);
|
Chris@12
|
195 $body = $this->createBodyFromJson($json);
|
Chris@12
|
196 return $toUpdate->withBody($body);
|
Chris@12
|
197 }
|
Chris@0
|
198 }
|