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