Chris@0: Chris@0: * JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES Chris@0: * Chris@0: * Chris@0: * @const int Chris@0: */ Chris@0: const DEFAULT_JSON_FLAGS = 79; Chris@0: Chris@0: /** Chris@12: * @var mixed Chris@12: */ Chris@12: private $payload; Chris@12: Chris@12: /** Chris@12: * @var int Chris@12: */ Chris@12: private $encodingOptions; Chris@12: Chris@12: /** Chris@0: * Create a JSON response with the given data. Chris@0: * Chris@0: * Default JSON encoding is performed with the following options, which Chris@0: * produces RFC4627-compliant JSON, capable of embedding into HTML. Chris@0: * Chris@0: * - JSON_HEX_TAG Chris@0: * - JSON_HEX_APOS Chris@0: * - JSON_HEX_AMP Chris@0: * - JSON_HEX_QUOT Chris@0: * - JSON_UNESCAPED_SLASHES Chris@0: * Chris@0: * @param mixed $data Data to convert to JSON. Chris@0: * @param int $status Integer status code for the response; 200 by default. Chris@0: * @param array $headers Array of headers to use at initialization. Chris@0: * @param int $encodingOptions JSON encoding options to use. Chris@0: * @throws InvalidArgumentException if unable to encode the $data to JSON. Chris@0: */ Chris@0: public function __construct( Chris@0: $data, Chris@0: $status = 200, Chris@0: array $headers = [], Chris@0: $encodingOptions = self::DEFAULT_JSON_FLAGS Chris@0: ) { Chris@12: $this->setPayload($data); Chris@12: $this->encodingOptions = $encodingOptions; Chris@12: Chris@12: $json = $this->jsonEncode($data, $this->encodingOptions); Chris@12: $body = $this->createBodyFromJson($json); Chris@0: Chris@0: $headers = $this->injectContentType('application/json', $headers); Chris@0: Chris@0: parent::__construct($body, $status, $headers); Chris@0: } Chris@0: Chris@0: /** Chris@12: * @return mixed Chris@12: */ Chris@12: public function getPayload() Chris@12: { Chris@12: return $this->payload; Chris@12: } Chris@12: Chris@12: /** Chris@12: * @param $data Chris@12: * Chris@12: * @return JsonResponse Chris@12: */ Chris@12: public function withPayload($data) Chris@12: { Chris@12: $new = clone $this; Chris@12: $new->setPayload($data); Chris@12: return $this->updateBodyFor($new); Chris@12: } Chris@12: Chris@12: /** Chris@12: * @return int Chris@12: */ Chris@12: public function getEncodingOptions() Chris@12: { Chris@12: return $this->encodingOptions; Chris@12: } Chris@12: Chris@12: /** Chris@12: * @param int $encodingOptions Chris@12: * Chris@12: * @return JsonResponse Chris@12: */ Chris@12: public function withEncodingOptions($encodingOptions) Chris@12: { Chris@12: $new = clone $this; Chris@12: $new->encodingOptions = $encodingOptions; Chris@12: return $this->updateBodyFor($new); Chris@12: } Chris@12: Chris@12: /** Chris@12: * @param string $json Chris@12: * Chris@12: * @return Stream Chris@12: */ Chris@12: private function createBodyFromJson($json) Chris@12: { Chris@12: $body = new Stream('php://temp', 'wb+'); Chris@12: $body->write($json); Chris@12: $body->rewind(); Chris@12: Chris@12: return $body; Chris@12: } Chris@12: Chris@12: /** Chris@0: * Encode the provided data to JSON. Chris@0: * Chris@0: * @param mixed $data Chris@0: * @param int $encodingOptions Chris@0: * @return string Chris@0: * @throws InvalidArgumentException if unable to encode the $data to JSON. Chris@0: */ Chris@0: private function jsonEncode($data, $encodingOptions) Chris@0: { Chris@0: if (is_resource($data)) { Chris@0: throw new InvalidArgumentException('Cannot JSON encode resources'); Chris@0: } Chris@0: Chris@0: // Clear json_last_error() Chris@0: json_encode(null); Chris@0: Chris@0: $json = json_encode($data, $encodingOptions); Chris@0: Chris@0: if (JSON_ERROR_NONE !== json_last_error()) { Chris@0: throw new InvalidArgumentException(sprintf( Chris@0: 'Unable to encode data to JSON in %s: %s', Chris@0: __CLASS__, Chris@0: json_last_error_msg() Chris@0: )); Chris@0: } Chris@0: Chris@0: return $json; Chris@0: } Chris@12: Chris@12: /** Chris@12: * @param $data Chris@12: */ Chris@12: private function setPayload($data) Chris@12: { Chris@12: if (is_object($data)) { Chris@12: $data = clone $data; Chris@12: } Chris@12: Chris@12: $this->payload = $data; Chris@12: } Chris@12: Chris@12: /** Chris@12: * Update the response body for the given instance. Chris@12: * Chris@12: * @param self $toUpdate Instance to update. Chris@12: * @return JsonResponse Returns a new instance with an updated body. Chris@12: */ Chris@12: private function updateBodyFor(self $toUpdate) Chris@12: { Chris@12: $json = $this->jsonEncode($toUpdate->payload, $toUpdate->encodingOptions); Chris@12: $body = $this->createBodyFromJson($json); Chris@12: return $toUpdate->withBody($body); Chris@12: } Chris@0: }