2e86c939
xu
“首次提交”
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
use DOMDocument;
use DOMElement;
use DOMText;
use DOMException;
use yii\base\Arrayable;
use yii\base\Component;
use yii\helpers\StringHelper;
/**
* XmlResponseFormatter formats the given data into an XML response content.
*
* It is used by [[Response]] to format response data.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class XmlResponseFormatter extends Component implements ResponseFormatterInterface
{
/**
* @var string the Content-Type header for the response
*/
public $contentType = 'application/xml';
/**
* @var string the XML version
*/
public $version = '1.0';
/**
* @var string the XML encoding. If not set, it will use the value of [[Response::charset]].
*/
public $encoding;
/**
* @var string the name of the root element. If set to false, null or is empty then no root tag should be added.
*/
public $rootTag = 'response';
/**
* @var string the name of the elements that represent the array elements with numeric keys.
*/
public $itemTag = 'item';
/**
* @var bool whether to interpret objects implementing the [[\Traversable]] interface as arrays.
* Defaults to `true`.
* @since 2.0.7
*/
public $useTraversableAsArray = true;
/**
* @var bool if object tags should be added
* @since 2.0.11
*/
public $useObjectTags = true;
/**
* Formats the specified response.
* @param Response $response the response to be formatted.
*/
public function format($response)
{
$charset = $this->encoding === null ? $response->charset : $this->encoding;
if (stripos($this->contentType, 'charset') === false) {
$this->contentType .= '; charset=' . $charset;
}
$response->getHeaders()->set('Content-Type', $this->contentType);
if ($response->data !== null) {
$dom = new DOMDocument($this->version, $charset);
if (!empty($this->rootTag)) {
$root = new DOMElement($this->rootTag);
$dom->appendChild($root);
$this->buildXml($root, $response->data);
} else {
$this->buildXml($dom, $response->data);
}
$response->content = $dom->saveXML();
}
}
/**
* @param DOMElement $element
* @param mixed $data
*/
protected function buildXml($element, $data)
{
if (is_array($data) ||
($data instanceof \Traversable && $this->useTraversableAsArray && !$data instanceof Arrayable)
) {
foreach ($data as $name => $value) {
if (is_int($name) && is_object($value)) {
$this->buildXml($element, $value);
} elseif (is_array($value) || is_object($value)) {
$child = new DOMElement($this->getValidXmlElementName($name));
$element->appendChild($child);
$this->buildXml($child, $value);
} else {
$child = new DOMElement($this->getValidXmlElementName($name));
$element->appendChild($child);
$child->appendChild(new DOMText($this->formatScalarValue($value)));
}
}
} elseif (is_object($data)) {
if ($this->useObjectTags) {
$child = new DOMElement(StringHelper::basename(get_class($data)));
$element->appendChild($child);
} else {
$child = $element;
}
if ($data instanceof Arrayable) {
$this->buildXml($child, $data->toArray());
} else {
$array = [];
foreach ($data as $name => $value) {
$array[$name] = $value;
}
$this->buildXml($child, $array);
}
} else {
$element->appendChild(new DOMText($this->formatScalarValue($data)));
}
}
/**
* Formats scalar value to use in XML text node
*
* @param int|string|bool $value
* @return string
* @since 2.0.11
*/
protected function formatScalarValue($value)
{
if ($value === true) {
return 'true';
}
if ($value === false) {
return 'false';
}
return (string) $value;
}
/**
* Returns element name ready to be used in DOMElement if
* name is not empty, is not int and is valid.
*
* Falls back to [[itemTag]] otherwise.
*
* @param mixed $name
* @return string
* @since 2.0.12
*/
protected function getValidXmlElementName($name)
{
if (empty($name) || is_int($name) || !$this->isValidXmlName($name)) {
return $this->itemTag;
}
return $name;
}
/**
* Checks if name is valid to be used in XML
*
* @param mixed $name
* @return bool
* @see http://stackoverflow.com/questions/2519845/how-to-check-if-string-is-a-valid-xml-element-name/2519943#2519943
* @since 2.0.12
*/
protected function isValidXmlName($name)
{
try {
new DOMElement($name);
return true;
} catch (DOMException $e) {
return false;
}
}
}
|