Body::arrayToXML()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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