Version 1
[yaffs-website] / vendor / consolidation / output-formatters / src / Transformations / DomToArraySimplifier.php
diff --git a/vendor/consolidation/output-formatters/src/Transformations/DomToArraySimplifier.php b/vendor/consolidation/output-formatters/src/Transformations/DomToArraySimplifier.php
new file mode 100644 (file)
index 0000000..d6771d0
--- /dev/null
@@ -0,0 +1,236 @@
+<?php
+namespace Consolidation\OutputFormatters\Transformations;
+
+use Consolidation\OutputFormatters\Options\FormatterOptions;
+use Consolidation\OutputFormatters\StructuredData\Xml\DomDataInterface;
+use Consolidation\OutputFormatters\StructuredData\Xml\XmlSchema;
+
+/**
+ * Simplify a DOMDocument to an array.
+ */
+class DomToArraySimplifier implements SimplifyToArrayInterface
+{
+    public function __construct()
+    {
+    }
+
+    /**
+     * @param ReflectionClass $dataType
+     */
+    public function canSimplify(\ReflectionClass $dataType)
+    {
+        return
+            $dataType->isSubclassOf('\Consolidation\OutputFormatters\StructuredData\Xml\DomDataInterface') ||
+            $dataType->isSubclassOf('DOMDocument') ||
+            ($dataType->getName() == 'DOMDocument');
+    }
+
+    public function simplifyToArray($structuredData, FormatterOptions $options)
+    {
+        if ($structuredData instanceof DomDataInterface) {
+            $structuredData = $structuredData->getDomData();
+        }
+        if ($structuredData instanceof \DOMDocument) {
+            // $schema = $options->getXmlSchema();
+            $simplified = $this->elementToArray($structuredData);
+            $structuredData = array_shift($simplified);
+        }
+        return $structuredData;
+    }
+
+    /**
+     * Recursively convert the provided DOM element into a php array.
+     *
+     * @param \DOMNode $element
+     * @return array
+     */
+    protected function elementToArray(\DOMNode $element)
+    {
+        if ($element->nodeType == XML_TEXT_NODE) {
+            return $element->nodeValue;
+        }
+        $attributes = $this->getNodeAttributes($element);
+        $children = $this->getNodeChildren($element);
+
+        return array_merge($attributes, $children);
+    }
+
+    /**
+     * Get all of the attributes of the provided element.
+     *
+     * @param \DOMNode $element
+     * @return array
+     */
+    protected function getNodeAttributes($element)
+    {
+        if (empty($element->attributes)) {
+            return [];
+        }
+        $attributes = [];
+        foreach ($element->attributes as $key => $attribute) {
+            $attributes[$key] = $attribute->nodeValue;
+        }
+        return $attributes;
+    }
+
+    /**
+     * Get all of the children of the provided element, with simplification.
+     *
+     * @param \DOMNode $element
+     * @return array
+     */
+    protected function getNodeChildren($element)
+    {
+        if (empty($element->childNodes)) {
+            return [];
+        }
+        $uniformChildrenName = $this->hasUniformChildren($element);
+        if ("{$uniformChildrenName}s" == $element->nodeName) {
+            $result = $this->getUniformChildren($element->nodeName, $element);
+        } else {
+            $result = $this->getUniqueChildren($element->nodeName, $element);
+        }
+        return array_filter($result);
+    }
+
+    /**
+     * Get the data from the children of the provided node in preliminary
+     * form.
+     *
+     * @param \DOMNode $element
+     * @return array
+     */
+    protected function getNodeChildrenData($element)
+    {
+        $children = [];
+        foreach ($element->childNodes as $key => $value) {
+            $children[$key] = $this->elementToArray($value);
+        }
+        return $children;
+    }
+
+    /**
+     * Determine whether the children of the provided element are uniform.
+     * @see getUniformChildren(), below.
+     *
+     * @param \DOMNode $element
+     * @return boolean
+     */
+    protected function hasUniformChildren($element)
+    {
+        $last = false;
+        foreach ($element->childNodes as $key => $value) {
+            $name = $value->nodeName;
+            if (!$name) {
+                return false;
+            }
+            if ($last && ($name != $last)) {
+                return false;
+            }
+            $last = $name;
+        }
+        return $last;
+    }
+
+    /**
+     * Convert the children of the provided DOM element into an array.
+     * Here, 'uniform' means that all of the element names of the children
+     * are identical, and further, the element name of the parent is the
+     * plural form of the child names.  When the children are uniform in
+     * this way, then the parent element name will be used as the key to
+     * store the children in, and the child list will be returned as a
+     * simple list with their (duplicate) element names omitted.
+     *
+     * @param string $parentKey
+     * @param \DOMNode $element
+     * @return array
+     */
+    protected function getUniformChildren($parentKey, $element)
+    {
+        $children = $this->getNodeChildrenData($element);
+        $simplifiedChildren = [];
+        foreach ($children as $key => $value) {
+            if ($this->valueCanBeSimplified($value)) {
+                $value = array_shift($value);
+            }
+            $id = $this->getIdOfValue($value);
+            if ($id) {
+                $simplifiedChildren[$parentKey][$id] = $value;
+            } else {
+                $simplifiedChildren[$parentKey][] = $value;
+            }
+        }
+        return $simplifiedChildren;
+    }
+
+    /**
+     * Determine whether the provided value has additional unnecessary
+     * nesting.  {"color": "red"} is converted to "red". No other
+     * simplification is done.
+     *
+     * @param \DOMNode $value
+     * @return boolean
+     */
+    protected function valueCanBeSimplified($value)
+    {
+        if (!is_array($value)) {
+            return false;
+        }
+        if (count($value) != 1) {
+            return false;
+        }
+        $data = array_shift($value);
+        return is_string($data);
+    }
+
+    /**
+     * If the object has an 'id' or 'name' element, then use that
+     * as the array key when storing this value in its parent.
+     * @param mixed $value
+     * @return string
+     */
+    protected function getIdOfValue($value)
+    {
+        if (!is_array($value)) {
+            return false;
+        }
+        if (array_key_exists('id', $value)) {
+            return trim($value['id'], '-');
+        }
+        if (array_key_exists('name', $value)) {
+            return trim($value['name'], '-');
+        }
+    }
+
+    /**
+     * Convert the children of the provided DOM element into an array.
+     * Here, 'unique' means that all of the element names of the children are
+     * different.  Since the element names will become the key of the
+     * associative array that is returned, so duplicates are not supported.
+     * If there are any duplicates, then an exception will be thrown.
+     *
+     * @param string $parentKey
+     * @param \DOMNode $element
+     * @return array
+     */
+    protected function getUniqueChildren($parentKey, $element)
+    {
+        $children = $this->getNodeChildrenData($element);
+        if ((count($children) == 1) && (is_string($children[0]))) {
+            return [$element->nodeName => $children[0]];
+        }
+        $simplifiedChildren = [];
+        foreach ($children as $key => $value) {
+            if (is_numeric($key) && is_array($value) && (count($value) == 1)) {
+                $valueKeys = array_keys($value);
+                $key = $valueKeys[0];
+                $value = array_shift($value);
+            }
+            if (array_key_exists($key, $simplifiedChildren)) {
+                throw new \Exception("Cannot convert data from a DOM document to an array, because <$key> appears more than once, and is not wrapped in a <{$key}s> element.");
+            }
+            $simplifiedChildren[$key] = $value;
+        }
+        return $simplifiedChildren;
+    }
+}