Xml.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Config\Reader;
  10. use XMLReader;
  11. use Zend\Config\Exception;
  12. /**
  13. * XML config reader.
  14. */
  15. class Xml implements ReaderInterface
  16. {
  17. /**
  18. * XML Reader instance.
  19. *
  20. * @var XMLReader
  21. */
  22. protected $reader;
  23. /**
  24. * Directory of the file to process.
  25. *
  26. * @var string
  27. */
  28. protected $directory;
  29. /**
  30. * Nodes to handle as plain text.
  31. *
  32. * @var array
  33. */
  34. protected $textNodes = array(
  35. XMLReader::TEXT,
  36. XMLReader::CDATA,
  37. XMLReader::WHITESPACE,
  38. XMLReader::SIGNIFICANT_WHITESPACE
  39. );
  40. /**
  41. * fromFile(): defined by Reader interface.
  42. *
  43. * @see ReaderInterface::fromFile()
  44. * @param string $filename
  45. * @return array
  46. * @throws Exception\RuntimeException
  47. */
  48. public function fromFile($filename)
  49. {
  50. if (!is_file($filename) || !is_readable($filename)) {
  51. throw new Exception\RuntimeException(sprintf(
  52. "File '%s' doesn't exist or not readable",
  53. $filename
  54. ));
  55. }
  56. $this->reader = new XMLReader();
  57. $this->reader->open($filename, null, LIBXML_XINCLUDE);
  58. $this->directory = dirname($filename);
  59. set_error_handler(
  60. function ($error, $message = '', $file = '', $line = 0) use ($filename) {
  61. throw new Exception\RuntimeException(
  62. sprintf('Error reading XML file "%s": %s', $filename, $message),
  63. $error
  64. );
  65. },
  66. E_WARNING
  67. );
  68. $return = $this->process();
  69. restore_error_handler();
  70. return $return;
  71. }
  72. /**
  73. * fromString(): defined by Reader interface.
  74. *
  75. * @see ReaderInterface::fromString()
  76. * @param string $string
  77. * @return array|bool
  78. * @throws Exception\RuntimeException
  79. */
  80. public function fromString($string)
  81. {
  82. if (empty($string)) {
  83. return array();
  84. }
  85. $this->reader = new XMLReader();
  86. $this->reader->xml($string, null, LIBXML_XINCLUDE);
  87. $this->directory = null;
  88. set_error_handler(
  89. function ($error, $message = '', $file = '', $line = 0) {
  90. throw new Exception\RuntimeException(
  91. sprintf('Error reading XML string: %s', $message),
  92. $error
  93. );
  94. },
  95. E_WARNING
  96. );
  97. $return = $this->process();
  98. restore_error_handler();
  99. return $return;
  100. }
  101. /**
  102. * Process data from the created XMLReader.
  103. *
  104. * @return array
  105. */
  106. protected function process()
  107. {
  108. return $this->processNextElement();
  109. }
  110. /**
  111. * Process the next inner element.
  112. *
  113. * @return mixed
  114. */
  115. protected function processNextElement()
  116. {
  117. $children = array();
  118. $text = '';
  119. while ($this->reader->read()) {
  120. if ($this->reader->nodeType === XMLReader::ELEMENT) {
  121. if ($this->reader->depth === 0) {
  122. return $this->processNextElement();
  123. }
  124. $attributes = $this->getAttributes();
  125. $name = $this->reader->name;
  126. if ($this->reader->isEmptyElement) {
  127. $child = array();
  128. } else {
  129. $child = $this->processNextElement();
  130. }
  131. if ($attributes) {
  132. if (!is_array($child)) {
  133. $child = array();
  134. }
  135. $child = array_merge($child, $attributes);
  136. }
  137. if (isset($children[$name])) {
  138. if (!is_array($children[$name]) || !array_key_exists(0, $children[$name])) {
  139. $children[$name] = array($children[$name]);
  140. }
  141. $children[$name][] = $child;
  142. } else {
  143. $children[$name] = $child;
  144. }
  145. } elseif ($this->reader->nodeType === XMLReader::END_ELEMENT) {
  146. break;
  147. } elseif (in_array($this->reader->nodeType, $this->textNodes)) {
  148. $text .= $this->reader->value;
  149. }
  150. }
  151. return $children ?: $text;
  152. }
  153. /**
  154. * Get all attributes on the current node.
  155. *
  156. * @return array
  157. */
  158. protected function getAttributes()
  159. {
  160. $attributes = array();
  161. if ($this->reader->hasAttributes) {
  162. while ($this->reader->moveToNextAttribute()) {
  163. $attributes[$this->reader->localName] = $this->reader->value;
  164. }
  165. $this->reader->moveToElement();
  166. }
  167. return $attributes;
  168. }
  169. }