Body::xml()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
nc 4
nop 3
dl 0
loc 12
rs 10
c 1
b 0
f 0
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 3.0.0
13
 */
14
15
namespace Quantum\Http\Traits\Response;
16
17
use Quantum\Http\Exceptions\HttpException;
18
use Quantum\App\Enums\ReservedKeys;
19
use Quantum\Http\Enums\ContentType;
20
use SimpleXMLElement;
21
use DOMDocument;
22
use Exception;
23
24
/**
25
 * Trait Body
26
 * @package Quantum\Http\Response
27
 */
28
trait Body
29
{
30
    /**
31
     * Response
32
     * @var array
33
     */
34
    private static $__response = [];
35
36
    /**
37
     * @var string[]
38
     */
39
    private static $formatters = [
40
        ContentType::HTML => 'formatHtml',
41
        ContentType::XML => 'formatXml',
42
        ContentType::JSON => 'formatJson',
43
        ContentType::JSONP => 'formatJsonp',
44
    ];
45
46
    /**
47
     * Checks if response contains a data by given key
48
     * @param string $key
49
     * @return bool
50
     */
51
    public static function has(string $key): bool
52
    {
53
        return isset(self::$__response[$key]);
54
    }
55
56
    /**
57
     * Gets the data from response by given key
58
     * @param string $key
59
     * @param string|null $default
60
     * @return mixed
61
     */
62
    public static function get(string $key, ?string $default = null)
63
    {
64
        return self::has($key) ? self::$__response[$key] : $default;
65
    }
66
67
    /**
68
     * Sets new key/value pair into response
69
     * @param string $key
70
     * @param mixed $value
71
     */
72
    public static function set(string $key, $value)
73
    {
74
        self::$__response[$key] = $value;
75
    }
76
77
    /**
78
     * Gets all response parameters
79
     * @return array
80
     */
81
    public static function all(): array
82
    {
83
        return self::$__response;
84
    }
85
86
    /**
87
     * Deletes the element from response by given key
88
     * @param string $key
89
     */
90
    public static function delete(string $key)
91
    {
92
        if (self::has($key)) {
93
            unset(self::$__response[$key]);
94
        }
95
    }
96
97
    /**
98
     * Prepares the JSON response
99
     * @param array|null $data
100
     * @param int|null $code
101
     */
102
    public static function json(?array $data = null, ?int $code = null)
103
    {
104
        self::setContentType(ContentType::JSON);
105
106
        if (!is_null($code)) {
107
            self::setStatusCode($code);
108
        }
109
110
        if ($data) {
111
            self::$__response = array_merge(self::$__response, $data);
112
        }
113
    }
114
115
    /**
116
     * Prepares the JSONP response
117
     * @param string $callback
118
     * @param array|null $data
119
     * @param int|null $code
120
     */
121
    public static function jsonp(string $callback, ?array $data = null, ?int $code = null)
122
    {
123
        self::setContentType(ContentType::JSONP);
124
125
        self::$callbackFunction = $callback;
0 ignored issues
show
Bug Best Practice introduced by
The property callbackFunction does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
126
127
        if (!is_null($code)) {
128
            self::setStatusCode($code);
129
        }
130
131
        if ($data) {
132
            self::$__response = array_merge(self::$__response, $data);
133
        }
134
    }
135
136
    /**
137
     * Returns response with function
138
     * @param array $data
139
     * @return string
140
     */
141
    public static function getJsonPData(array $data): string
142
    {
143
        return self::$callbackFunction . '(' . json_encode($data) . ')';
144
    }
145
146
    /**
147
     * Prepares the XML response
148
     * @param array|null $data
149
     * @param int|null $code
150
     */
151
    public static function xml(?array $data = null, $root = '<data></data>', ?int $code = null)
152
    {
153
        self::setContentType(ContentType::XML);
154
155
        if (!is_null($code)) {
156
            self::setStatusCode($code);
157
        }
158
159
        self::$xmlRoot = $root;
0 ignored issues
show
Bug Best Practice introduced by
The property xmlRoot does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
160
161
        if ($data) {
162
            self::$__response = array_merge(self::$__response, $data);
163
        }
164
    }
165
166
    /**
167
     * Prepares the HTML content
168
     * @param string $html
169
     * @param int|null $code
170
     */
171
    public static function html(string $html, ?int $code = null)
172
    {
173
        self::setContentType(ContentType::HTML);
174
175
        if (!is_null($code)) {
176
            self::setStatusCode($code);
177
        }
178
179
        self::$__response[ReservedKeys::RENDERED_VIEW] = $html;
180
    }
181
182
    /**
183
     * Gets the response content
184
     * @return string
185
     * @throws HttpException
186
     */
187
    public static function getContent(): string
188
    {
189
        $contentType = self::getContentType();
0 ignored issues
show
Bug introduced by
The method getContentType() does not exist on Quantum\Http\Traits\Response\Body. Did you maybe mean getContent()? ( Ignorable by Annotation )

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

189
        /** @scrutinizer ignore-call */ 
190
        $contentType = self::getContentType();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
190
191
        if (!isset(self::$formatters[$contentType])) {
192
            throw new HttpException("Unsupported content type: {$contentType}");
193
        }
194
195
        $formatterMethod = self::$formatters[$contentType];
196
197
        return self::$formatterMethod();
198
    }
199
200
    /**
201
     * Transforms array to XML
202
     * @param array $arr
203
     * @return string
204
     * @throws Exception
205
     */
206
    private static function arrayToXML(array $arr): string
207
    {
208
        $simpleXML = new SimpleXMLElement(self::$xmlRoot);
209
        self::composeXML($arr, $simpleXML);
210
211
        $dom = new DOMDocument();
212
        $dom->loadXML($simpleXML->asXML());
213
        $dom->formatOutput = true;
214
        return $dom->saveXML();
215
    }
216
217
    /**
218
     * Compose XML
219
     * @param array $arr
220
     * @param SimpleXMLElement $simpleXML
221
     */
222
    private static function composeXML(array $arr, SimpleXMLElement &$simpleXML)
223
    {
224
        foreach ($arr as $key => $value) {
225
            if (is_numeric($key)) {
226
                $key = 'item' . $key;
227
            }
228
229
            $tag = $key;
230
            $attributes = null;
231
232
            if (strpos($key, '@') !== false) {
233
                [$tag, $attributes] = explode('@', $key);
234
                $attributes = json_decode($attributes);
235
            }
236
237
            if (is_array($value)) {
238
                $child = $simpleXML->addChild($tag);
239
                if ($attributes) {
240
                    foreach ($attributes as $attrKey => $attrVal) {
241
                        $child->addAttribute($attrKey, $attrVal);
242
                    }
243
                }
244
245
                self::composeXML($value, $child);
246
            } else {
247
                $child = $simpleXML->addChild($tag, htmlspecialchars($value));
248
249
                if ($attributes) {
250
                    foreach ($attributes as $attrKey => $attrVal) {
251
                        $child->addAttribute($attrKey, $attrVal);
252
                    }
253
                }
254
            }
255
        }
256
    }
257
258
    /**
259
     * Formats data as JSON
260
     * @return string
261
     */
262
    private static function formatJson(): string
263
    {
264
        return json_encode(self::all(), JSON_UNESCAPED_UNICODE);
265
    }
266
267
    /**
268
     * Formats data as XML
269
     * @return string
270
     * @throws Exception
271
     */
272
    private static function formatXml(): string
273
    {
274
        return self::arrayToXml(self::all());
275
    }
276
277
    /**
278
     * Formats data as HTML
279
     * @return string
280
     */
281
    private static function formatHtml(): string
282
    {
283
        return self::get(ReservedKeys::RENDERED_VIEW) ?? '';
284
    }
285
286
    /**
287
     * Formats data as JSONP
288
     * @return string
289
     */
290
    private static function formatJsonp(): string
291
    {
292
        return self::getJsonPData(self::all());
293
    }
294
}
295