Passed
Pull Request — master (#42)
by Arman
03:27
created

HttpResponse::composeXML()   B

Complexity

Conditions 9
Paths 21

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
c 0
b 0
f 0
dl 0
loc 30
rs 8.0555
cc 9
nc 21
nop 2
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.4.0
13
 */
14
15
namespace Quantum\Http\Response;
16
17
use Quantum\Exceptions\HttpException;
18
use Quantum\Bootstrap;
19
use SimpleXMLElement;
20
use DOMDocument;
21
22
/**
23
 * Class HttpResponse
24
 * @package Quantum\Http\Response
25
 */
26
abstract class HttpResponse
27
{
28
29
    use Header;
30
    use Body;
31
32
    /**
33
     * HTML content type
34
     */
35
    const CONTENT_HTML = 'text/html';
36
37
    /**
38
     * XML content type
39
     */
40
    const CONTENT_XML = 'application/xml';
41
42
    /**
43
     * JSON content type
44
     */
45
    const CONTENT_JSON = 'application/json';
46
47
    /**
48
     * Status code
49
     * @var int
50
     */
51
    private static $__statusCode = 200;
52
53
    /**
54
     * XML root element
55
     * @var string
56
     */
57
    private static $xmlRoot = '<data></data>';
58
59
    /**
60
     * Status texts
61
     * @var array
62
     */
63
    public static $statusTexts = [];
64
65
    /**
66
     * Initialize the Response
67
     * @param bool $test
68
     * @throws \Quantum\Exceptions\HttpException
69
     */
70
    public static function init($test = false)
71
    {
72
        if (!$test && get_caller_class(3) !== Bootstrap::class) {
73
            throw new HttpException(HttpException::UNEXPECTED_RESPONSE_INITIALIZATION);
74
        }
75
76
        self::$statusTexts = self::$statusTexts ?: require_once 'statuses.php';
77
    }
78
79
    /**
80
     * Flushes the response header and body
81
     */
82
    public static function flush()
83
    {
84
        self::$__statusCode = 200;
85
        self::$__headers = [];
86
        self::$__response = [];
87
    }
88
89
    /**
90
     * Sends all response data to the client and finishes the request.
91
     */
92
    public static function send()
93
    {
94
        foreach (self::$__headers as $key => $value) {
95
            header($key . ': ' . $value);
96
        }
97
98
        echo self::getContent();
99
    }
100
101
    /**
102
     * Gets the response content
103
     * @return string
104
     * @throws \Exception
105
     */
106
    public static function getContent(): string
107
    {
108
        $content = '';
109
110
        switch (self::getContentType()) {
111
            case self::CONTENT_JSON:
112
                $content = json_encode(self::all());
113
                break;
114
            case self::CONTENT_XML:
115
                $content = self::arrayToXml(self::all());
116
                break;
117
            case self::CONTENT_HTML:
118
                $content = self::get('_qt_rendered_view');
119
                break;
120
            default :
121
                break;
122
        }
123
124
        return $content;
125
    }
126
127
    /**
128
     * Set the status code
129
     * @param int $code
130
     */
131
    public static function setStatusCode(int $code)
132
    {
133
        if (!array_key_exists($code, self::$statusTexts)) {
134
            throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
135
        }
136
137
        self::$__statusCode = $code;
138
    }
139
140
    /**
141
     * Gets the status code
142
     * @return int
143
     */
144
    public static function getStatusCode(): int
145
    {
146
        return self::$__statusCode;
147
    }
148
149
    /**
150
     * Gets the status text
151
     * @return string
152
     */
153
    public static function getStatusText(): string
154
    {
155
        return self::$statusTexts[self::$__statusCode];
156
    }
157
158
    /**
159
     * Redirect
160
     * @param string $url
161
     * @param int|null $code
162
     * @throws \Quantum\Exceptions\StopExecutionException
163
     */
164
    public static function redirect(string $url, int $code = null)
165
    {
166
        if (!is_null($code)) {
167
            self::setStatusCode($code);
168
        }
169
170
        self::setHeader('Location', $url);
171
172
        stop();
173
    }
174
175
    /**
176
     * Prepares the JSON response
177
     * @param array|null $data
178
     * @param int|null $code
179
     */
180
    public static function json(array $data = null, int $code = null)
181
    {
182
        self::setContentType(self::CONTENT_JSON);
183
184
        if (!is_null($code)) {
185
            self::setStatusCode($code);
186
        }
187
188
        if ($data) {
189
            foreach ($data as $key => $value) {
190
                self::$__response[$key] = $value;
191
            }
192
        }
193
    }
194
195
    /**
196
     * Prepares the XML response
197
     * @param array|null $data
198
     * @param int|null $code
199
     */
200
    public static function xml(array $data = null, $root = '<data></data>', int $code = null)
201
    {
202
        self::setContentType(self::CONTENT_XML);
203
204
        self::$xmlRoot = $root;
205
206
        if (!is_null($code)) {
207
            self::setStatusCode($code);
208
        }
209
210
        if ($data) {
211
            foreach ($data as $key => $value) {
212
                self::$__response[$key] = $value;
213
            }
214
        }
215
    }
216
217
    /**
218
     * Prepares the HTML content
219
     * @param string $html
220
     * @param int|null $code
221
     */
222
    public static function html(string $html, int $code = null)
223
    {
224
        self::setContentType(self::CONTENT_HTML);
225
226
        if (!is_null($code)) {
227
            self::setStatusCode($code);
228
        }
229
230
        self::$__response['_qt_rendered_view'] = $html;
231
    }
232
233
    /**
234
     * Transforms array to XML
235
     * @param array $arr
236
     * @return string
237
     * @throws \Exception
238
     */
239
    private static function arrayToXML(array $arr): string
240
    {
241
        $simpleXML = new SimpleXMLElement(self::$xmlRoot);
242
        self::composeXML($arr, $simpleXML);
243
244
        $dom = new DOMDocument();
245
        $dom->loadXML($simpleXML->asXML());
246
        $dom->formatOutput = true;
247
        return $dom->saveXML();
248
    }
249
250
    /**
251
     * Compose XML
252
     * @param array $arr
253
     * @param \SimpleXMLElement $simpleXML
254
     */
255
    private static function composeXML(array $arr, SimpleXMLElement &$simpleXML)
256
    {
257
        foreach ($arr as $key => $value) {
258
            if (is_numeric($key)) {
259
                $key = 'item' . $key;
260
            }
261
262
            $tag = $key;
263
            $attributes = null;
264
265
            if (strpos($key, '@') !== false) {
266
                list($tag, $attributes) = explode('@', $key);
267
                $attributes = json_decode($attributes);
268
            }
269
270
            if (is_array($value)) {
271
                $child = $simpleXML->addChild($tag);
272
                if ($attributes) {
273
                    foreach ($attributes as $attrKey => $attrVal) {
274
                        $child->addAttribute($attrKey, $attrVal);
275
                    }
276
                }
277
278
                self::composeXML($value, $child);
279
            } else {
280
                $child = $simpleXML->addChild($tag, htmlspecialchars($value));
281
282
                if ($attributes) {
283
                    foreach ($attributes as $attrKey => $attrVal) {
284
                        $child->addAttribute($attrKey, $attrVal);
285
                    }
286
                }
287
            }
288
        }
289
    }
290
291
}
292