1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Symfony\Component\Config\Util;
13:
14: 15: 16: 17: 18: 19: 20: 21:
22: class XmlUtils
23: {
24: 25: 26:
27: private function __construct()
28: {
29: }
30:
31: 32: 33: 34: 35: 36: 37: 38: 39: 40:
41: public static function loadFile($file, $schemaOrCallable = null)
42: {
43: $internalErrors = libxml_use_internal_errors(true);
44: $disableEntities = libxml_disable_entity_loader(true);
45: libxml_clear_errors();
46:
47: $dom = new \DOMDocument();
48: $dom->validateOnParse = true;
49: if (!$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
50: libxml_disable_entity_loader($disableEntities);
51:
52: throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors)));
53: }
54:
55: $dom->normalizeDocument();
56:
57: libxml_use_internal_errors($internalErrors);
58: libxml_disable_entity_loader($disableEntities);
59:
60: foreach ($dom->childNodes as $child) {
61: if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
62: throw new \InvalidArgumentException('Document types are not allowed.');
63: }
64: }
65:
66: if (null !== $schemaOrCallable) {
67: $internalErrors = libxml_use_internal_errors(true);
68: libxml_clear_errors();
69:
70: $e = null;
71: if (is_callable($schemaOrCallable)) {
72: try {
73: $valid = call_user_func($schemaOrCallable, $dom, $internalErrors);
74: } catch (\Exception $e) {
75: $valid = false;
76: }
77: } elseif (!is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) {
78: $valid = @$dom->schemaValidate($schemaOrCallable);
79: } else {
80: libxml_use_internal_errors($internalErrors);
81:
82: throw new \InvalidArgumentException('The schemaOrCallable argument has to be a valid path to XSD file or callable.');
83: }
84:
85: if (!$valid) {
86: $messages = static::getXmlErrors($internalErrors);
87: if (empty($messages)) {
88: $messages = array(sprintf('The XML file "%s" is not valid.', $file));
89: }
90: throw new \InvalidArgumentException(implode("\n", $messages), 0, $e);
91: }
92:
93: libxml_use_internal_errors($internalErrors);
94: }
95:
96: return $dom;
97: }
98:
99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118:
119: public static function convertDomElementToArray(\DomElement $element, $checkPrefix = true)
120: {
121: $prefix = (string) $element->prefix;
122: $empty = true;
123: $config = array();
124: foreach ($element->attributes as $name => $node) {
125: if ($checkPrefix && !in_array((string) $node->prefix, array('', $prefix), true)) {
126: continue;
127: }
128: $config[$name] = static::phpize($node->value);
129: $empty = false;
130: }
131:
132: $nodeValue = false;
133: foreach ($element->childNodes as $node) {
134: if ($node instanceof \DOMText) {
135: if (trim($node->nodeValue)) {
136: $nodeValue = trim($node->nodeValue);
137: $empty = false;
138: }
139: } elseif ($checkPrefix && $prefix != (string) $node->prefix) {
140: continue;
141: } elseif (!$node instanceof \DOMComment) {
142: $value = static::convertDomElementToArray($node, $checkPrefix);
143:
144: $key = $node->localName;
145: if (isset($config[$key])) {
146: if (!is_array($config[$key]) || !is_int(key($config[$key]))) {
147: $config[$key] = array($config[$key]);
148: }
149: $config[$key][] = $value;
150: } else {
151: $config[$key] = $value;
152: }
153:
154: $empty = false;
155: }
156: }
157:
158: if (false !== $nodeValue) {
159: $value = static::phpize($nodeValue);
160: if (count($config)) {
161: $config['value'] = $value;
162: } else {
163: $config = $value;
164: }
165: }
166:
167: return !$empty ? $config : null;
168: }
169:
170: 171: 172: 173: 174: 175: 176:
177: public static function phpize($value)
178: {
179: $value = (string) $value;
180: $lowercaseValue = strtolower($value);
181:
182: switch (true) {
183: case 'null' === $lowercaseValue:
184: return null;
185: case ctype_digit($value):
186: $raw = $value;
187: $cast = intval($value);
188:
189: return '0' == $value[0] ? octdec($value) : (((string) $raw == (string) $cast) ? $cast : $raw);
190: case 'true' === $lowercaseValue:
191: return true;
192: case 'false' === $lowercaseValue:
193: return false;
194: case is_numeric($value):
195: return '0x' == $value[0].$value[1] ? hexdec($value) : floatval($value);
196: case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $value):
197: return floatval(str_replace(',', '', $value));
198: default:
199: return $value;
200: }
201: }
202:
203: protected static function getXmlErrors($internalErrors)
204: {
205: $errors = array();
206: foreach (libxml_get_errors() as $error) {
207: $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
208: LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
209: $error->code,
210: trim($error->message),
211: $error->file ? $error->file : 'n/a',
212: $error->line,
213: $error->column
214: );
215: }
216:
217: libxml_clear_errors();
218: libxml_use_internal_errors($internalErrors);
219:
220: return $errors;
221: }
222: }
223: