Hoa central
Xml.php
Go to the documentation of this file.
1 <?php
2 
37 namespace Hoa\Xml;
38 
39 use Hoa\Core;
40 use Hoa\Stream;
41 
50 abstract class Xml
51  extends Stream\Composite
52  implements Element,
54  \Countable,
55  \IteratorAggregate,
56  \ArrayAccess
57 {
63  protected $_namespaces = null;
64 
70  protected $_errors = null;
71 
72 
73 
92  public function __construct(
93  $stream,
94  Stream $innerStream,
95  $initializeNamespace = true,
96  $entityResolver = null
97  ) {
98  if (!function_exists('simplexml_load_file')) {
99  throw new Exception(
100  'SimpleXML must be enable for using %s.',
101  0,
102  get_class($this)
103  );
104  }
105 
106  if (null !== $entityResolver) {
107  $entityResolver = xcallable($entityResolver);
108  }
109 
110  libxml_use_internal_errors(true);
111 
112  if (PHP_VERSION_ID >= 50400) {
113  libxml_set_external_entity_loader(
114  function ($public, $system, $context) use (&$entityResolver) {
115  if (null === $entityResolver) {
116  return null;
117  }
118 
119  return $entityResolver($public, $system, $context);
120  }
121  );
122  } else {
123  libxml_disable_entity_loader(true);
124  }
125 
126  if ($innerStream instanceof Stream\IStream\In) {
127  if ($innerStream instanceof Stream\IStream\Pointable) {
128  $innerStream->rewind();
129  }
130 
131  $handle = $innerStream->readAll();
132  $root = @simplexml_load_string($handle, $stream);
133  } else {
134  $root = @simplexml_load_file($innerStream->getStreamName(), $stream);
135  }
136 
137  $this->_errors = libxml_get_errors();
138  $this->clearErrors();
139 
140  if (false === $root || true === $this->hasError()) {
141  if (false === $this->hasError()) {
142  throw new Exception(
143  'Failed to open the XML document %s.',
144  1,
145  $innerStream->getStreamName()
146  );
147  }
148 
149  $errors = $this->getErrors();
150  $first = array_shift($errors);
151  $message =
152  ' • ' . trim(ucfirst($first->message)) .
153  ' (at line ' . $first->line .
154  ', column ' . $first->column . ')';
155 
156  foreach ($errors as $error) {
157  $message .=
158  ';' . "\n" .
159  ' • ' . trim(ucfirst($error->message)) .
160  ' (at line ' . $error->line .
161  ', column ' . $error->column . ')';
162  }
163 
164  $message .= '.' . "\n";
165 
166  if ($innerStream instanceof Stream\IStream\In) {
167  $xml = explode("\n", $innerStream->readAll());
168 
169  if (!empty($xml[0])) {
170  $message .=
171  "\n" . 'You should take a look at ' .
172  'this piece of code: ' . "\n";
173  $lines = count($xml) - 1;
174  $line = $first->line;
175  $foo = strlen((string) ($line + 3));
176 
177  for (
178  $i = max(1, $line - 3), $m = min($lines, $line + 3);
179  $i <= $m;
180  ++$i
181  ) {
182  $message .= sprintf('%' . $foo . 'd', $i) . '. ';
183 
184  if ($i == $line) {
185  $message .= '➜ ';
186  } else {
187  $message .= ' ';
188  }
189 
190  $message .= $xml[$i - 1] . "\n";
191  }
192  }
193  }
194 
195  throw new Exception(
196  'Errors occured while parsing the XML document %s:' .
197  "\n" . '%s',
198  2,
199  [$innerStream->getStreamName(), $message]
200  );
201  }
202 
203  $this->setStream($root);
204  $this->setInnerStream($innerStream);
205 
206  if (true === $initializeNamespace) {
207  $this->initializeNamespaces();
208  }
209 
210  return;
211  }
212 
221  public function initializeNamespaces()
222  {
223  $stream = $this->getStream();
224  $this->_namespaces = $stream->getDocNamespaces();
225 
226  if (empty($this->_namespaces)) {
227  throw new Exception\NamespaceMissing(
228  'The XML document %s must have a default namespace at least.',
229  4,
230  $this->getInnerStream()->getStreamName()
231  );
232  }
233 
234  if (1 == count($this->_namespaces)) {
235  $stream->registerXPathNamespace(
236  '__current_ns',
237  current($this->_namespaces)
238  );
239  } else {
240  foreach ($this->_namespaces as $prefix => $namespace) {
241  if ('' == $prefix) {
242  $prefix = '__current_ns';
243  }
244 
245  $stream->registerXPathNamespace($prefix, $namespace);
246  }
247  }
248 
249  return;
250  }
251 
258  public function namespaceExists($namespace)
259  {
260  return false !== array_search($namespace, $this->_namespaces);
261  }
262 
270  public function useNamespace($namespace)
271  {
272  if (null === $this->_namespaces) {
273  $this->initializeNamespaces();
274  }
275 
276  if (false === $prefix = array_search($namespace, $this->_namespaces)) {
277  throw new Exception(
278  'The namespace %s does not exist in the document %s.',
279  5,
280  [$namespace, $this->getInnerStream()->getStreamName()]
281  );
282  }
283 
284  $this->getStream()->registerXPathNamespace('__current_ns', $namespace);
285 
286  return $this;
287  }
288 
296  public function getPrefix($namespace)
297  {
298  if (false === $prefix = array_search($namespace, $this->_namespaces)) {
299  throw new Exception(
300  'The namespace %s does not exist in the document %s.',
301  6,
302  [$namespace, $this->getInnerStream()->getStreamName()]
303  );
304  }
305 
306  return $prefix;
307  }
308 
314  public function getNamespaces()
315  {
316  if (null === $this->_namespaces) {
317  $this->initializeNamespaces();
318  }
319 
320  return $this->_namespaces;
321  }
322 
328  public function selectRoot()
329  {
330  return $this->getStream()->selectRoot();
331  }
332 
338  public function selectAnyElements()
339  {
340  return $this->getStream()->selectAnyElements();
341  }
342 
349  public function selectElements($E = null)
350  {
351  return $this->getStream()->selectElements($E);
352  }
353 
360  public function selectDescendantElements($F = null)
361  {
362  return $this->getStream()->selectDescendantElements($F);
363  }
364 
371  public function selectChildElements($F = null)
372  {
373  return $this->getStream()->selectChildElements($F);
374  }
375 
382  public function selectAdjacentSiblingElement($F)
383  {
384  return $this->getStream()->selectAdjacentSiblingElement($F);
385  }
386 
393  public function selectSiblingElements($F = null)
394  {
395  return $this->getStream()->selectSiblingElements($F);
396  }
397 
405  public function querySelector($query)
406  {
407  return $this->getStream()->querySelector($query);
408  }
409 
417  public function querySelectorAll($query)
418  {
419  return $this->getStream()->querySelectorAll($query);
420  }
421 
428  public function xpath($path)
429  {
430  return $this->getStream()->xpath($path);
431  }
432 
438  public function __toString()
439  {
440  return $this->getStream()->__toString();
441  }
442 
448  public function readAttributes()
449  {
450  return $this->getStream()->readAttributes();
451  }
452 
459  public function readAttribute($name)
460  {
461  return $this->getStream()->readAttribute($name);
462  }
463 
470  public function attributeExists($name)
471  {
472  return $this->getStream()->attributeExists($name);
473  }
474 
480  public function readAttributesAsList()
481  {
482  return $this->getStream()->readAttributesAsList();
483  }
484 
491  public function readAttributeAsList($name)
492  {
493  return $this->getStream()->readAttributeAsList($name);
494  }
495 
501  public function readAttributesAsString()
502  {
503  return $this->getStream()->readAttributesAsString();
504  }
505 
511  public function readXML()
512  {
513  return $this->getStream()->asXML();
514  }
515 
521  public function readDOM()
522  {
523  return $this->getStream()->readDOM();
524  }
525 
531  public function getName()
532  {
533  return $this->getStream()->getName();
534  }
535 
541  public function count()
542  {
543  return $this->getStream()->count();
544  }
545 
551  public function getIterator()
552  {
553  return $this->getStream();
554  }
555 
563  public function __set($name, $value)
564  {
565  $this->getStream()->$name = $value;
566 
567  return;
568  }
569 
576  public function __get($name)
577  {
578  return $this->getStream()->$name;
579  }
580 
587  public function offsetExists($offset)
588  {
589  return null !== $this->readAttribute($offset);
590  }
591 
598  public function offsetGet($offset)
599  {
600  return $this->readAttribute($offset);
601  }
602 
610  public function offsetSet($offset, $value)
611  {
612  $handle = $this->getStream();
613  $handle[$offset] = $value;
614 
615  return;
616  }
617 
624  public function offsetUnset($offset)
625  {
626  $handle = $this->getStream();
627  unset($handle[$offset]);
628 
629  return;
630  }
631 
637  public function hasError()
638  {
639  return !empty($this->_errors);
640  }
641 
647  public function getErrors()
648  {
649  return $this->_errors;
650  }
651 
657  protected function clearErrors()
658  {
659  return libxml_clear_errors();
660  }
661 }
662 
666 Core\Consistency::flexEntity('Hoa\Xml\Xml');
selectRoot()
Definition: Xml.php:328
readAttributeAsList($name)
Definition: Xml.php:491
offsetGet($offset)
Definition: Xml.php:598
getIterator()
Definition: Xml.php:551
offsetUnset($offset)
Definition: Xml.php:624
$_namespaces
Definition: Xml.php:63
__set($name, $value)
Definition: Xml.php:563
xpath($path)
Definition: Xml.php:428
setInnerStream(Stream $innerStream)
Definition: Composite.php:95
readAttribute($name)
Definition: Xml.php:459
offsetSet($offset, $value)
Definition: Xml.php:610
offsetExists($offset)
Definition: Xml.php:587
__toString()
Definition: Xml.php:438
querySelector($query)
Definition: Xml.php:405
readDOM()
Definition: Xml.php:521
useNamespace($namespace)
Definition: Xml.php:270
hasError()
Definition: Xml.php:637
initializeNamespaces()
Definition: Xml.php:221
namespaceExists($namespace)
Definition: Xml.php:258
selectChildElements($F=null)
Definition: Xml.php:371
selectDescendantElements($F=null)
Definition: Xml.php:360
readXML()
Definition: Xml.php:511
getPrefix($namespace)
Definition: Xml.php:296
selectAdjacentSiblingElement($F)
Definition: Xml.php:382
getName()
Definition: Xml.php:531
readAttributes()
Definition: Xml.php:448
readAttributesAsList()
Definition: Xml.php:480
count()
Definition: Xml.php:541
__construct($stream, Stream $innerStream, $initializeNamespace=true, $entityResolver=null)
Definition: Xml.php:92
selectSiblingElements($F=null)
Definition: Xml.php:393
getNamespaces()
Definition: Xml.php:314
clearErrors()
Definition: Xml.php:657
__get($name)
Definition: Xml.php:576
getErrors()
Definition: Xml.php:647
readAttributesAsString()
Definition: Xml.php:501
selectElements($E=null)
Definition: Xml.php:349
selectAnyElements()
Definition: Xml.php:338
attributeExists($name)
Definition: Xml.php:470
querySelectorAll($query)
Definition: Xml.php:417
$_errors
Definition: Xml.php:70