1 <?php namespace Modulework\Modules\Http;
2 /*
3 * (c) Christian Gärtner <christiangaertner.film@googlemail.com>
4 * This file is part of the Modulework Framework
5 * License: View distributed LICENSE file
6 */
7
8 use stdClass;
9 use InvalidArgumentException;
10 use Modulework\Modules\Http\Utilities\HeaderWrapperInterface;
11
12 /**
13 * JSON-Response
14 * A HTTP Response in the JSON format (application/json)
15 */
16 class JsonResponse extends Response
17 {
18 const CONTENTTYPE_JS = 'application/javascript';
19 const CONTENTTYPE_JSON = 'application/json';
20 protected $callback;
21 protected $json;
22 protected $rawdata;
23
24 /**
25 * Factory for the Response object
26 * @param mixed $content The data which gets encoded to json
27 * @param integer $code The HTTP status code
28 * @param array $headers The HTTP headers
29 *
30 * @param \Modulework\Modules\Http\Utilities\HeaderWrapperInterface | null $headerWrapper The wrapper for PHP' s native header releated functions
31 *
32 * @return \Modulework\Modules\Http\JsonResponse The new JsonResponse object
33 *
34 * @throws InvalidArgumentException (from Constructor)
35 */
36 public static function make($json = null, $code = 200, array $headers = array(), HeaderWrapperInterface $headerWrapper = null)
37 {
38 return new static($json, $code, $headers, $headerWrapper);
39 }
40
41 /**
42 * Constructor.
43 * @param mixed $content The data which gets encoded to json
44 * @param integer $code The HTTP status code
45 * @param array $headers The HTTP headers
46 *
47 * @param \Modulework\Modules\Http\Utilities\HeaderWrapperInterface | null $headerWrapper The wrapper for PHP' s native header releated functions
48 *
49 * @return \Modulework\Modules\Http\JsonResponse The new JsonResponse object
50 *
51 * @throws InvalidArgumentException (from setContent)
52 */
53 public function __construct($json = null, $code = 200, array $headers = array(), HeaderWrapperInterface $headerWrapper = null)
54 {
55 parent::__construct('', $code, $headers, $headerWrapper);
56
57 if ($json === null) {
58 $json = new stdClass;
59 }
60
61 $this->setJson($json);
62 }
63
64 /**
65 * Set the json data for the response
66 * @param mixed $json The data (a JSON string is possible as well) which gets encoded
67 * @param boolean $json_string Whether the input doesn' t need to get encoded
68 *
69 * @return \Modulework\Modules\Http\JsonResponse THIS
70 */
71 public function setJson($json = array(), $json_string = false)
72 {
73 $this->rawdata = $json;
74 $this->json = ($json_string) ? $json : json_encode($json);
75
76 return $this->refresh();
77 }
78
79 /**
80 * Returns the json data
81 * @param boolean $raw Whether it shoud return the raw or encoded data
82 *
83 * @return mixed|string The json data
84 */
85 public function getJson($raw = false)
86 {
87 return ($raw) ? $this->rawdata : $this->json;
88 }
89
90 /**
91 * Set the json callback for the response
92 * @param string $callback The callback
93 *
94 * @return \Modulework\Modules\Http\JsonResponse THIS
95 *
96 * @throws \InvalidArgumentException
97 */
98 public function setCallback($callback = null)
99 {
100 if ($callback !== null) {
101 if (!self::isValidIdentifier($callback)) {
102 throw new InvalidArgumentException('This identifier is not valid!');
103 }
104 }
105
106 $this->callback = $callback;
107
108 return $this->refresh();
109 }
110
111 /**
112 * Returns the callback
113 * @return string The callback
114 */
115 public function getCallback()
116 {
117 return $this->callback;
118 }
119
120 /**
121 * Updates all parameters (changing headers and setting content)
122 *
123 * @return \Modulework\Modules\Http\JsonResponse THIS
124 */
125 protected function refresh()
126 {
127 if ($this->callback !== null) {
128 $this->headers->set('Content-Type', self::CONTENTTYPE_JS);
129
130 // This will produce "CALLBACK(JSON);"
131 return $this->setContent($this->callback . '(' . $this->json . ');');
132 }
133
134 if (!$this->headers->has('Content-Type') || $this->headers->get('Content-Type') === self::CONTENTTYPE_JS) {
135 $this->headers->set('Content-Type', self::CONTENTTYPE_JSON);
136 }
137
138 return $this->setContent($this->json);
139 }
140
141 /**
142 * Checks if the string is a valid javascript identifier
143 * Taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier
144 * Formatting modified + minor changes
145 * @param string $callback The string to check
146 * @return boolean Whether the string is valid
147 */
148 protected static function isValidIdentifier($callback)
149 {
150 $identifierSyntax = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u';
151 $reservedWords = array(
152 'break',
153 'do',
154 'instanceof',
155 'typeof',
156 'case',
157 'else',
158 'new',
159 'var',
160 'catch',
161 'finally',
162 'return',
163 'void',
164 'continue',
165 'for',
166 'switch',
167 'while',
168 'debugger',
169 'function',
170 'this',
171 'with',
172 'default',
173 'if',
174 'throw',
175 'delete',
176 'in',
177 'try',
178 'class',
179 'enum',
180 'extends',
181 'super',
182 'const',
183 'export',
184 'import',
185 'implements',
186 'let',
187 'private',
188 'public',
189 'yield',
190 'interface',
191 'package',
192 'protected',
193 'static',
194 'null',
195 'true',
196 'false'
197 );
198
199 return (preg_match($identifierSyntax, $callback) && !in_array(mb_strtolower($callback, 'UTF-8'), $reservedWords));
200 }
201 }