composer package updates
[openemr.git] / vendor / symfony / config / Util / XmlUtils.php
blobc7c3157180221988a1845c19a22c1a54edc60cfd
1 <?php
3 /*
4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Config\Util;
14 /**
15 * XMLUtils is a bunch of utility methods to XML operations.
17 * This class contains static methods only and is not meant to be instantiated.
19 * @author Fabien Potencier <fabien@symfony.com>
20 * @author Martin HasoĊˆ <martin.hason@gmail.com>
22 class XmlUtils
24 /**
25 * This class should not be instantiated.
27 private function __construct()
31 /**
32 * Loads an XML file.
34 * @param string $file An XML file path
35 * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation
37 * @return \DOMDocument
39 * @throws \InvalidArgumentException When loading of XML file returns error
40 * @throws \RuntimeException When DOM extension is missing
42 public static function loadFile($file, $schemaOrCallable = null)
44 if (!extension_loaded('dom')) {
45 throw new \RuntimeException('Extension DOM is required.');
48 $content = @file_get_contents($file);
49 if ('' === trim($content)) {
50 throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file));
53 $internalErrors = libxml_use_internal_errors(true);
54 $disableEntities = libxml_disable_entity_loader(true);
55 libxml_clear_errors();
57 $dom = new \DOMDocument();
58 $dom->validateOnParse = true;
59 if (!$dom->loadXML($content, LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
60 libxml_disable_entity_loader($disableEntities);
62 throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors)));
65 $dom->normalizeDocument();
67 libxml_use_internal_errors($internalErrors);
68 libxml_disable_entity_loader($disableEntities);
70 foreach ($dom->childNodes as $child) {
71 if (XML_DOCUMENT_TYPE_NODE === $child->nodeType) {
72 throw new \InvalidArgumentException('Document types are not allowed.');
76 if (null !== $schemaOrCallable) {
77 $internalErrors = libxml_use_internal_errors(true);
78 libxml_clear_errors();
80 $e = null;
81 if (is_callable($schemaOrCallable)) {
82 try {
83 $valid = call_user_func($schemaOrCallable, $dom, $internalErrors);
84 } catch (\Exception $e) {
85 $valid = false;
87 } elseif (!is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) {
88 $schemaSource = file_get_contents((string) $schemaOrCallable);
89 $valid = @$dom->schemaValidateSource($schemaSource);
90 } else {
91 libxml_use_internal_errors($internalErrors);
93 throw new \InvalidArgumentException('The schemaOrCallable argument has to be a valid path to XSD file or callable.');
96 if (!$valid) {
97 $messages = static::getXmlErrors($internalErrors);
98 if (empty($messages)) {
99 $messages = array(sprintf('The XML file "%s" is not valid.', $file));
101 throw new \InvalidArgumentException(implode("\n", $messages), 0, $e);
105 libxml_clear_errors();
106 libxml_use_internal_errors($internalErrors);
108 return $dom;
112 * Converts a \DOMElement object to a PHP array.
114 * The following rules applies during the conversion:
116 * * Each tag is converted to a key value or an array
117 * if there is more than one "value"
119 * * The content of a tag is set under a "value" key (<foo>bar</foo>)
120 * if the tag also has some nested tags
122 * * The attributes are converted to keys (<foo foo="bar"/>)
124 * * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>)
126 * @param \DOMElement $element A \DOMElement instance
127 * @param bool $checkPrefix Check prefix in an element or an attribute name
129 * @return array A PHP array
131 public static function convertDomElementToArray(\DOMElement $element, $checkPrefix = true)
133 $prefix = (string) $element->prefix;
134 $empty = true;
135 $config = array();
136 foreach ($element->attributes as $name => $node) {
137 if ($checkPrefix && !in_array((string) $node->prefix, array('', $prefix), true)) {
138 continue;
140 $config[$name] = static::phpize($node->value);
141 $empty = false;
144 $nodeValue = false;
145 foreach ($element->childNodes as $node) {
146 if ($node instanceof \DOMText) {
147 if ('' !== trim($node->nodeValue)) {
148 $nodeValue = trim($node->nodeValue);
149 $empty = false;
151 } elseif ($checkPrefix && $prefix != (string) $node->prefix) {
152 continue;
153 } elseif (!$node instanceof \DOMComment) {
154 $value = static::convertDomElementToArray($node, $checkPrefix);
156 $key = $node->localName;
157 if (isset($config[$key])) {
158 if (!is_array($config[$key]) || !is_int(key($config[$key]))) {
159 $config[$key] = array($config[$key]);
161 $config[$key][] = $value;
162 } else {
163 $config[$key] = $value;
166 $empty = false;
170 if (false !== $nodeValue) {
171 $value = static::phpize($nodeValue);
172 if (count($config)) {
173 $config['value'] = $value;
174 } else {
175 $config = $value;
179 return !$empty ? $config : null;
183 * Converts an xml value to a PHP type.
185 * @param mixed $value
187 * @return mixed
189 public static function phpize($value)
191 $value = (string) $value;
192 $lowercaseValue = strtolower($value);
194 switch (true) {
195 case 'null' === $lowercaseValue:
196 return;
197 case ctype_digit($value):
198 $raw = $value;
199 $cast = (int) $value;
201 return '0' == $value[0] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw);
202 case isset($value[1]) && '-' === $value[0] && ctype_digit(substr($value, 1)):
203 $raw = $value;
204 $cast = (int) $value;
206 return '0' == $value[1] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw);
207 case 'true' === $lowercaseValue:
208 return true;
209 case 'false' === $lowercaseValue:
210 return false;
211 case isset($value[1]) && '0b' == $value[0].$value[1]:
212 return bindec($value);
213 case is_numeric($value):
214 return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value;
215 case preg_match('/^0x[0-9a-f]++$/i', $value):
216 return hexdec($value);
217 case preg_match('/^(-|\+)?[0-9]+(\.[0-9]+)?$/', $value):
218 return (float) $value;
219 default:
220 return $value;
224 protected static function getXmlErrors($internalErrors)
226 $errors = array();
227 foreach (libxml_get_errors() as $error) {
228 $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
229 LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
230 $error->code,
231 trim($error->message),
232 $error->file ?: 'n/a',
233 $error->line,
234 $error->column
238 libxml_clear_errors();
239 libxml_use_internal_errors($internalErrors);
241 return $errors;