Completed
Push — master ( a8b099...15a87c )
by Raffael
02:55
created

Response::terminate()   A

Complexity

Conditions 1
Paths 0

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 0
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
4
/**
5
 * Micro
6
 *
7
 * @author    Raffael Sahli <[email protected]>
8
 * @copyright Copyright (c) 2017 gyselroth GmbH (https://gyselroth.com)
9
 * @license   MIT https://opensource.org/licenses/MIT
10
 */
11
12
namespace Micro\Http;
13
14
use \Micro\Http\Http;
15
use \Closure;
16
use \SimpleXMLElement;
17
18
class Response
19
{
20
    /**
21
     * Output format
22
     *
23
     * @var string
24
     */
25
    protected $output_format = 'json';
26
27
28
    /**
29
     * Possible output formats
30
     */
31
    const OUTPUT_FORMATS = [
32
        'json' => 'application/json; charset=utf-8',
33
        'xml'  => 'application/xml; charset=utf-8',
34
        'text' => 'text/html; charset=utf-8'
35
    ];
36
37
38
    /**
39
     * Human readable output
40
     *
41
     * @var bool
42
     */
43
    protected $pretty_format = false;
44
45
46
    /**
47
     * Headers
48
     *
49
     * @var array
50
     */
51
    protected $headers = [];
52
53
54
    /**
55
     * Code
56
     *
57
     * @var int
58
     */
59
    protected $code = 200;
60
61
62
    /**
63
     * Body
64
     *
65
     * @var string
66
     */
67
    protected $body;
68
69
70
    /**
71
     * Init response
72
     *
73
     * @return void
74
     */
75
    public function __construct()
76
    {
77
        $this->setupFormats();
78
    }
79
80
81
    /**
82
     * Set header
83
     *
84
     * @param   string $header
85
     * @param   string $value
86
     * @return  Response
87
     */
88
    public function setHeader(string $header, string $value): Response
89
    {
90
        $this->headers[$header] = $value;
91
        return $this;
92
    }
93
94
95
    /**
96
     * Delete header
97
     *
98
     * @param   string $header
99
     * @return  Response
100
     */
101
    public function removeHeader(string $header): Response
102
    {
103
        if(isset($this->headers[$header])) {
104
            unset($this->headers[$header]);
105
        }
106
107
        return $this;
108
    }
109
110
111
    /**
112
     * Get headers
113
     *
114
     * @return array
115
     */
116
    public function getHeaders(): array
117
    {
118
        return $this->headers;
119
    }
120
121
122
    /**
123
     * Send headers
124
     *
125
     * @return  Response
126
     */
127
    public function sendHeaders(): Response
128
    {
129
        foreach ($this->headers as $header => $value) {
130
            header($header.': '.$value);
131
        }
132
133
        return $this;
134
    }
135
136
137
    /**
138
     * Set response code
139
     *
140
     * @param   int $code
141
     * @return  Response
142
     */
143
    public function setCode(int $code): Response
144
    {
145
        if (!array_key_exists($code, Http::STATUS_CODES)) {
146
            throw new Exception('invalid http code set');
147
        }
148
149
        $this->code = $code;
150
        return $this;
151
    }
152
153
154
    /**
155
     * Get response code
156
     *
157
     * @return int
158
     */
159
    public function getCode(): int
160
    {
161
        return $this->code;
162
    }
163
164
165
    /**
166
     * Set body
167
     *
168
     * @param  mixed $body
169
     * @return Response
170
     */
171
    public function setBody($body): Response
172
    {
173
        $this->body = $body;
174
        //$this->body_only = $body_only;
175
        $this->setOutputFormat($this->output_format);
176
177
        return $this;
178
    }
179
180
181
    /**
182
     * Get body
183
     *
184
     * @return string
185
     */
186
    public function getBody()
187
    {
188
        return $this->body;
189
    }
190
191
192
    /**
193
     * Sends the actual response.
194
     *
195
     * @return  void
196
     */
197
    public function send(): void
198
    {
199
        $status = Http::STATUS_CODES[$this->code];
200
        $this->sendHeaders();
201
        header('HTTP/1.0 '.$this->code.' '.$status, true, $this->code);
202
203
        if ($this->body === null || $this->code == 204) {
204
            return;
205
        }
206
207
        if($this->body instanceof Closure) {
0 ignored issues
show
introduced by
The condition $this->body instanceof Closure can never be true since $this->body is never a sub-type of Closure.
Loading history...
208
            $body = $this->body->call($this);
209
        } else {
210
            $body = $this->body;
211
        }
212
213
        /*if ($this->body_only === false && $this->output_format !== 'text') {
214
            $body = ['data' => $body];
215
            $body['status'] = intval($this->code);
216
            $body = array_reverse($body, true);
217
        }*/
218
219
        switch ($this->output_format) {
220
            case null:
221
            break;
222
223
            default:
224
            case 'json':
225
                echo $this->asJSON($body);
226
            break;
227
228
            case 'xml':
229
                echo $this->asXML($body);
230
            break;
231
232
            case 'text':
233
                echo $body;
234
            break;
235
        }
236
    }
237
238
239
    /**
240
     * Get output format
241
     *
242
     * @return string
243
     */
244
    public function getOutputFormat(): string
245
    {
246
        return $this->output_format;
247
    }
248
249
250
    /**
251
     * Convert response to human readable output
252
     *
253
     * @param   bool $format
254
     * @return  Response
255
     */
256
    public function setPrettyFormat(bool $format): Response
257
    {
258
        $this->pretty_format = (bool)$format;
259
        return $this;
260
    }
261
262
263
    /**
264
     * Set header Content-Length $body.
265
     *
266
     * @param  string $body
267
     * @return Response
268
     */
269
    public function setContentLength(string $body): Response
270
    {
271
        header('Content-Length: '.strlen($body));
272
        return $this;
273
    }
274
275
276
    /**
277
     * Converts $body to pretty json.
278
     *
279
     * @param  mixed $body
280
     * @return string
281
     */
282
    public function asJSON($body): string
283
    {
284
        if ($this->pretty_format) {
285
            $result = json_encode($body, JSON_PRETTY_PRINT);
286
        } else {
287
            $result = json_encode($body);
288
        }
289
290
        $this->setContentLength($result);
291
292
        return $result;
293
    }
294
295
296
    /**
297
     * Converts mixed data to XML
298
     *
299
     * @param    mixed $data
300
     * @param    SimpleXMLElement $xml
301
     * @param    string $child_name
302
     * @return   string
303
     */
304
    public function toXML($data, SimpleXMLElement $xml, string $child_name): string
305
    {
306
        if (is_array($data)) {
307
            foreach ($data as $k => $v) {
308
                if (is_array($v)) {
309
                    (is_int($k)) ? $this->toXML($v, $xml->addChild($child_name), $v) : $this->toXML($v, $xml->addChild(strtolower($k)), $child_name);
0 ignored issues
show
Bug introduced by
$v of type array is incompatible with the type string expected by parameter $child_name of Micro\Http\Response::toXML(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

309
                    (is_int($k)) ? $this->toXML($v, $xml->addChild($child_name), /** @scrutinizer ignore-type */ $v) : $this->toXML($v, $xml->addChild(strtolower($k)), $child_name);
Loading history...
310
                } else {
311
                    (is_int($k)) ? $xml->addChild($child_name, $v) : $xml->addChild(strtolower($k), $v);
312
                }
313
            }
314
        } else {
315
            $xml->addChild($child_name, $data);
316
        }
317
318
        return $xml->asXML();
319
    }
320
321
322
    /**
323
     * Converts response to xml.
324
     *
325
     * @param   mixed $body
326
     * @return  string
327
     */
328
    public function asXML($body): string
329
    {
330
        $root = new SimpleXMLElement('<response></response>');
331
        $raw = $this->toXML($body, $root, 'node');
332
333
        if ($this->pretty_format) {
334
            $raw = $this->prettyXml($raw);
335
        }
336
337
        $this->setContentLength($raw);
338
        return $raw;
339
    }
340
341
342
    /**
343
     * Pretty formatted xml
344
     *
345
     * @param   string $xml
346
     * @return  string
347
     */
348
    public function prettyXml(string $xml): string
349
    {
350
        $domxml = new \DOMDocument('1.0');
351
        $domxml->preserveWhiteSpace = false;
352
        $domxml->formatOutput = true;
353
        $domxml->loadXML($xml);
354
355
        return $domxml->saveXML();
356
    }
357
358
359
    /**
360
     * Set the current output format.
361
     *
362
     * @param  string $format
363
     * @return Response
364
     */
365
    public function setOutputFormat(?string $format=null): Response
366
    {
367
        if($format === null) {
368
            $this->output_format = null;
369
            return $this->removeHeader('Content-Type');
370
        }
371
372
        if(!array_key_exists($format, self::OUTPUT_FORMATS)) {
373
            throw new Exception('invalid output format given');
374
        }
375
376
        $this->setHeader('Content-Type', self::OUTPUT_FORMATS[$format]);
377
        $this->output_format = $format;
378
        return $this;
379
    }
380
381
382
    /**
383
     * Setup formats.
384
     *
385
     * @return Response
386
     */
387
    public function setupFormats(): Response
388
    {
389
        $pretty = array_key_exists('pretty', $_GET) && ($_GET['pretty'] != 'false' && $_GET['pretty'] != '0');
390
        $this->setPrettyFormat($pretty);
391
392
        //through HTTP_ACCEPT
393
        if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], '*/*') === false) {
394
            foreach (self::OUTPUT_FORMATS as $format) {
395
                if (strpos($_SERVER['HTTP_ACCEPT'], $format) !== false) {
396
                    $this->output_format = $format;
397
                    break;
398
                }
399
            }
400
        }
401
402
        return $this;
403
    }
404
}
405