aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/sabre/xml/lib/Reader.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sabre/xml/lib/Reader.php')
-rw-r--r--vendor/sabre/xml/lib/Reader.php308
1 files changed, 308 insertions, 0 deletions
diff --git a/vendor/sabre/xml/lib/Reader.php b/vendor/sabre/xml/lib/Reader.php
new file mode 100644
index 000000000..7cba76c59
--- /dev/null
+++ b/vendor/sabre/xml/lib/Reader.php
@@ -0,0 +1,308 @@
+<?php
+
+namespace Sabre\Xml;
+
+use XMLReader;
+
+/**
+ * The Reader class expands upon PHP's built-in XMLReader.
+ *
+ * The intended usage, is to assign certain XML elements to PHP classes. These
+ * need to be registered using the $elementMap public property.
+ *
+ * After this is done, a single call to parse() will parse the entire document,
+ * and delegate sub-sections of the document to element classes.
+ *
+ * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Reader extends XMLReader {
+
+ use ContextStackTrait;
+
+ /**
+ * Returns the current nodename in clark-notation.
+ *
+ * For example: "{http://www.w3.org/2005/Atom}feed".
+ * Or if no namespace is defined: "{}feed".
+ *
+ * This method returns null if we're not currently on an element.
+ *
+ * @return string|null
+ */
+ function getClark() {
+
+ if (! $this->localName) {
+ return null;
+ }
+
+ return '{' . $this->namespaceURI . '}' . $this->localName;
+
+ }
+
+ /**
+ * Reads the entire document.
+ *
+ * This function returns an array with the following three elements:
+ * * name - The root element name.
+ * * value - The value for the root element.
+ * * attributes - An array of attributes.
+ *
+ * This function will also disable the standard libxml error handler (which
+ * usually just results in PHP errors), and throw exceptions instead.
+ *
+ * @return array
+ */
+ function parse() {
+
+ $previousEntityState = libxml_disable_entity_loader(true);
+ $previousSetting = libxml_use_internal_errors(true);
+
+ // Really sorry about the silence operator, seems like I have no
+ // choice. See:
+ //
+ // https://bugs.php.net/bug.php?id=64230
+ while ($this->nodeType !== self::ELEMENT && @$this->read()) {
+ // noop
+ }
+ $result = $this->parseCurrentElement();
+
+ $errors = libxml_get_errors();
+ libxml_clear_errors();
+ libxml_use_internal_errors($previousSetting);
+ libxml_disable_entity_loader($previousEntityState);
+
+ if ($errors) {
+ throw new LibXMLException($errors);
+ }
+
+ return $result;
+ }
+
+
+
+ /**
+ * parseGetElements parses everything in the current sub-tree,
+ * and returns a an array of elements.
+ *
+ * Each element has a 'name', 'value' and 'attributes' key.
+ *
+ * If the the element didn't contain sub-elements, an empty array is always
+ * returned. If there was any text inside the element, it will be
+ * discarded.
+ *
+ * If the $elementMap argument is specified, the existing elementMap will
+ * be overridden while parsing the tree, and restored after this process.
+ *
+ * @param array $elementMap
+ * @return array
+ */
+ function parseGetElements(array $elementMap = null) {
+
+ $result = $this->parseInnerTree($elementMap);
+ if (!is_array($result)) {
+ return [];
+ }
+ return $result;
+
+ }
+
+ /**
+ * Parses all elements below the current element.
+ *
+ * This method will return a string if this was a text-node, or an array if
+ * there were sub-elements.
+ *
+ * If there's both text and sub-elements, the text will be discarded.
+ *
+ * If the $elementMap argument is specified, the existing elementMap will
+ * be overridden while parsing the tree, and restored after this process.
+ *
+ * @param array $elementMap
+ * @return array|string
+ */
+ function parseInnerTree(array $elementMap = null) {
+
+ $text = null;
+ $elements = [];
+
+ if ($this->nodeType === self::ELEMENT && $this->isEmptyElement) {
+ // Easy!
+ $this->next();
+ return null;
+ }
+
+ if (!is_null($elementMap)) {
+ $this->pushContext();
+ $this->elementMap = $elementMap;
+ }
+
+ // Really sorry about the silence operator, seems like I have no
+ // choice. See:
+ //
+ // https://bugs.php.net/bug.php?id=64230
+ if (!@$this->read()) return false;
+
+ while (true) {
+
+ if (!$this->isValid()) {
+
+ $errors = libxml_get_errors();
+
+ if ($errors) {
+ libxml_clear_errors();
+ throw new LibXMLException($errors);
+ }
+ }
+
+ switch ($this->nodeType) {
+ case self::ELEMENT :
+ $elements[] = $this->parseCurrentElement();
+ break;
+ case self::TEXT :
+ case self::CDATA :
+ $text .= $this->value;
+ $this->read();
+ break;
+ case self::END_ELEMENT :
+ // Ensuring we are moving the cursor after the end element.
+ $this->read();
+ break 2;
+ case self::NONE :
+ throw new ParseException('We hit the end of the document prematurely. This likely means that some parser "eats" too many elements. Do not attempt to continue parsing.');
+ default :
+ // Advance to the next element
+ $this->read();
+ break;
+ }
+
+ }
+
+ if (!is_null($elementMap)) {
+ $this->popContext();
+ }
+ return ($elements ? $elements : $text);
+
+ }
+
+ /**
+ * Reads all text below the current element, and returns this as a string.
+ *
+ * @return string
+ */
+ function readText() {
+
+ $result = '';
+ $previousDepth = $this->depth;
+
+ while ($this->read() && $this->depth != $previousDepth) {
+ if (in_array($this->nodeType, [XMLReader::TEXT, XMLReader::CDATA, XMLReader::WHITESPACE])) {
+ $result .= $this->value;
+ }
+ }
+ return $result;
+
+ }
+
+ /**
+ * Parses the current XML element.
+ *
+ * This method returns arn array with 3 properties:
+ * * name - A clark-notation XML element name.
+ * * value - The parsed value.
+ * * attributes - A key-value list of attributes.
+ *
+ * @return array
+ */
+ function parseCurrentElement() {
+
+ $name = $this->getClark();
+
+ $attributes = [];
+
+ if ($this->hasAttributes) {
+ $attributes = $this->parseAttributes();
+ }
+
+ $value = call_user_func(
+ $this->getDeserializerForElementName($name),
+ $this
+ );
+
+ return [
+ 'name' => $name,
+ 'value' => $value,
+ 'attributes' => $attributes,
+ ];
+ }
+
+
+ /**
+ * Grabs all the attributes from the current element, and returns them as a
+ * key-value array.
+ *
+ * If the attributes are part of the same namespace, they will simply be
+ * short keys. If they are defined on a different namespace, the attribute
+ * name will be retured in clark-notation.
+ *
+ * @return array
+ */
+ function parseAttributes() {
+
+ $attributes = [];
+
+ while ($this->moveToNextAttribute()) {
+ if ($this->namespaceURI) {
+
+ // Ignoring 'xmlns', it doesn't make any sense.
+ if ($this->namespaceURI === 'http://www.w3.org/2000/xmlns/') {
+ continue;
+ }
+
+ $name = $this->getClark();
+ $attributes[$name] = $this->value;
+
+ } else {
+ $attributes[$this->localName] = $this->value;
+ }
+ }
+ $this->moveToElement();
+
+ return $attributes;
+
+ }
+
+ /**
+ * Returns the function that should be used to parse the element identified
+ * by it's clark-notation name.
+ *
+ * @param string $name
+ * @return callable
+ */
+ function getDeserializerForElementName($name) {
+
+ if (!array_key_exists($name, $this->elementMap)) {
+ return ['Sabre\\Xml\\Element\\Base', 'xmlDeserialize'];
+ }
+
+ $deserializer = $this->elementMap[$name];
+ if (is_subclass_of($deserializer, 'Sabre\\Xml\\XmlDeserializable')) {
+ return [ $deserializer, 'xmlDeserialize' ];
+ }
+
+ if (is_callable($deserializer)) {
+ return $deserializer;
+ }
+
+ $type = gettype($deserializer);
+ if ($type === 'string') {
+ $type .= ' (' . $deserializer . ')';
+ } elseif ($type === 'object') {
+ $type .= ' (' . get_class($deserializer) . ')';
+ }
+ throw new \LogicException('Could not use this type as a deserializer: ' . $type . ' for element: ' . $name);
+
+ }
+
+}