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