Completed
Push — 2.1 ( 7c8525...0afc41 )
by Alexander
21:05 queued 16:02
created

XmlResponseFormatter::buildXml()   C

Complexity

Conditions 14
Paths 11

Size

Total Lines 38
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 14

Importance

Changes 0
Metric Value
dl 0
loc 38
ccs 26
cts 26
cp 1
rs 5.0864
c 0
b 0
f 0
cc 14
eloc 29
nc 11
nop 2
crap 14

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\web;
9
10
use DOMDocument;
11
use DOMElement;
12
use DOMText;
13
use DOMException;
14
use yii\base\Arrayable;
15
use yii\base\Component;
16
use yii\helpers\StringHelper;
17
18
/**
19
 * XmlResponseFormatter formats the given data into an XML response content.
20
 *
21
 * It is used by [[Response]] to format response data.
22
 *
23
 * @author Qiang Xue <[email protected]>
24
 * @since 2.0
25
 */
26
class XmlResponseFormatter extends Component implements ResponseFormatterInterface
27
{
28
    /**
29
     * @var string the Content-Type header for the response
30
     */
31
    public $contentType = 'application/xml';
32
    /**
33
     * @var string the XML version
34
     */
35
    public $version = '1.0';
36
    /**
37
     * @var string the XML encoding. If not set, it will use the value of [[Response::charset]].
38
     */
39
    public $encoding;
40
    /**
41
     * @var string the name of the root element. If set to false, null or is empty then no root tag should be added.
42
     */
43
    public $rootTag = 'response';
44
    /**
45
     * @var string the name of the elements that represent the array elements with numeric keys.
46
     */
47
    public $itemTag = 'item';
48
    /**
49
     * @var bool whether to interpret objects implementing the [[\Traversable]] interface as arrays.
50
     * Defaults to `true`.
51
     * @since 2.0.7
52
     */
53
    public $useTraversableAsArray = true;
54
    /**
55
     * @var bool if object tags should be added
56
     * @since 2.0.11
57
     */
58
    public $useObjectTags = true;
59
60
61
    /**
62
     * Formats the specified response.
63
     * @param Response $response the response to be formatted.
64
     */
65 20
    public function format($response)
66
    {
67 20
        $charset = $this->encoding === null ? $response->charset : $this->encoding;
68 20
        if (stripos($this->contentType, 'charset') === false) {
69 20
            $this->contentType .= '; charset=' . $charset;
70
        }
71 20
        $response->getHeaders()->set('Content-Type', $this->contentType);
72 20
        if ($response->data !== null) {
73 19
            $dom = new DOMDocument($this->version, $charset);
74 19
            if (!empty($this->rootTag)) {
75 18
                $root = new DOMElement($this->rootTag);
76 18
                $dom->appendChild($root);
77 18
                $this->buildXml($root, $response->data);
78
            } else {
79 1
                $this->buildXml($dom, $response->data);
0 ignored issues
show
Documentation introduced by
$dom is of type object<DOMDocument>, but the function expects a object<DOMElement>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
80
            }
81 19
            $response->content = $dom->saveXML();
82
        }
83 20
    }
84
85
    /**
86
     * @param DOMElement $element
87
     * @param mixed $data
88
     */
89 19
    protected function buildXml($element, $data)
90
    {
91 19
        if (is_array($data) ||
92 13
            ($data instanceof \Traversable && $this->useTraversableAsArray && !$data instanceof Arrayable)
93
        ) {
94 12
            foreach ($data as $name => $value) {
95 11
                if (is_int($name) && is_object($value)) {
96 3
                    $this->buildXml($element, $value);
97 11
                } elseif (is_array($value) || is_object($value)) {
98 3
                    $child = new DOMElement($this->getValidXmlElementName($name));
99 3
                    $element->appendChild($child);
100 3
                    $this->buildXml($child, $value);
101
                } else {
102 11
                    $child = new DOMElement($this->getValidXmlElementName($name));
103 11
                    $element->appendChild($child);
104 12
                    $child->appendChild(new DOMText($this->formatScalarValue($value)));
105
                }
106
            }
107 13
        } elseif (is_object($data)) {
108 6
            if ($this->useObjectTags) {
109 5
                $child = new DOMElement(StringHelper::basename(get_class($data)));
110 5
                $element->appendChild($child);
111
            } else {
112 1
                $child = $element;
113
            }
114 6
            if ($data instanceof Arrayable) {
115 1
                $this->buildXml($child, $data->toArray());
116
            } else {
117 5
                $array = [];
118 5
                foreach ($data as $name => $value) {
119 5
                    $array[$name] = $value;
120
                }
121 5
                $this->buildXml($child, $array);
122
            }
123
        } else {
124 7
            $element->appendChild(new DOMText($this->formatScalarValue($data)));
125
        }
126 19
    }
127
128
    /**
129
     * Formats scalar value to use in XML text node
130
     *
131
     * @param int|string|bool $value
132
     * @return string
133
     * @since 2.0.11
134
     */
135 18
    protected function formatScalarValue($value)
136
    {
137 18
        if ($value === true) {
138 2
            return 'true';
139
        }
140
141 17
        if ($value === false) {
142 2
            return 'false';
143
        }
144
145 16
        return (string) $value;
146
    }
147
148
    /**
149
     * Returns element name ready to be used in DOMElement if
150
     * name is not empty, is not int and is valid.
151
     *
152
     * Falls back to [[itemTag]] otherwise.
153
     *
154
     * @param mixed $name
155
     * @return string
156
     * @since 2.0.12
157
     */
158 11
    protected function getValidXmlElementName($name)
159
    {
160 11
        if (empty($name) || is_int($name) || !$this->isValidXmlName($name)) {
161 4
            return $this->itemTag;
162
        }
163 9
        return $name;
164
    }
165
166
    /**
167
     * Checks if name is valid to be used in XML
168
     *
169
     * @param mixed $name
170
     * @return bool
171
     * @see http://stackoverflow.com/questions/2519845/how-to-check-if-string-is-a-valid-xml-element-name/2519943#2519943
172
     * @since 2.0.12
173
     */
174 9
    protected function isValidXmlName($name)
175
    {
176
        try {
177 9
            new DOMElement($name);
178 9
            return true;
179 1
        } catch (DOMException $e) {
180 1
            return false;
181
        }
182
    }
183
}
184