From eca67375281a8f7f0bf2cd786d4622d2941a3644 Mon Sep 17 00:00:00 2001 From: Roel Date: Sat, 21 Apr 2018 03:19:39 +0200 Subject: [PATCH 01/10] Extract regex patterns to its own class --- src/PhpArrayToXml.php | 51 ++++------------------------------------- src/lib/XmlPatterns.php | 50 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 47 deletions(-) create mode 100644 src/lib/XmlPatterns.php diff --git a/src/PhpArrayToXml.php b/src/PhpArrayToXml.php index af4a1e7..03c33d1 100644 --- a/src/PhpArrayToXml.php +++ b/src/PhpArrayToXml.php @@ -4,6 +4,7 @@ use DOMDocument; use DOMElement; +use RefactorStudio\PhpArrayToXml\Lib\XmlPatterns; class PhpArrayToXml { @@ -340,7 +341,7 @@ public function getCastNullValue() */ public static function hasValidXmlTagStartingChar($value = null) { - if (preg_match(self::getValidXmlTagStartPattern(), $value) === 1) { + if (preg_match(XmlPatterns::getValidXmlTagStartPattern(), $value) === 1) { return true; } return false; @@ -354,7 +355,7 @@ public static function hasValidXmlTagStartingChar($value = null) */ public static function isValidXmlTagChar($value = null) { - if (preg_match(self::getValidXmlTagNameChar(), $value) === 1) { + if (preg_match(XmlPatterns::getValidXmlTagNameChar(), $value) === 1) { return true; } return false; @@ -372,7 +373,7 @@ public static function isValidXmlTag($value = null) return false; } - if (preg_match(self::getValidXmlTagNamePattern(), $value) === 1) { + if (preg_match(XmlPatterns::getValidXmlTagNamePattern(), $value) === 1) { return true; } return false; @@ -398,50 +399,6 @@ public function toXmlString($array = []) return $this->_doc->saveXML(); } - /** - * Get a regex pattern for valid tag names - * - * @return string - */ - protected static function getValidXmlTagNamePattern() - { - return '~ - # XML 1.0 Name symbol PHP PCRE regex - (?(DEFINE) - (? [:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]) - (? (?&NameStartChar) | [.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]) - (? (?&NameStartChar) (?&NameChar)*) - ) - ^(?&Name)$ - ~ux'; - } - - /** - * Get a regex pattern for valid tag chars - * - * @return string - */ - protected static function getValidXmlTagNameChar() - { - return '~ - (?(DEFINE) - (? [:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]) - (? (?&NameStartChar) | [.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]) - ) - ^(?&NameChar)$ - ~ux'; - } - - /** - * Get a regex pattern for valid tag starting characters - * - * @return string - */ - protected static function getValidXmlTagStartPattern() - { - return '~^([:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}])~ux'; - } - /** * Converts arrays to DOMDocument elements * diff --git a/src/lib/XmlPatterns.php b/src/lib/XmlPatterns.php new file mode 100644 index 0000000..86a4d1b --- /dev/null +++ b/src/lib/XmlPatterns.php @@ -0,0 +1,50 @@ + + (?(DEFINE) + (? [:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]) + (? (?&NameStartChar) | [.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]) + (? (?&NameStartChar) (?&NameChar)*) + ) + ^(?&Name)$ + ~ux'; + } + + /** + * Get a regex pattern for valid tag chars + * + * @return string + */ + public static function getValidXmlTagNameChar() + { + return '~ + (?(DEFINE) + (? [:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]) + (? (?&NameStartChar) | [.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]) + ) + ^(?&NameChar)$ + ~ux'; + } + + /** + * Get a regex pattern for valid tag starting characters + * + * @return string + */ + public static function getValidXmlTagStartPattern() + { + return '~^([:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}])~ux'; + } +} \ No newline at end of file From b535b322b5d2e78bab1c66524b0a609ad66c0b90 Mon Sep 17 00:00:00 2001 From: Roel Date: Sat, 21 Apr 2018 03:25:55 +0200 Subject: [PATCH 02/10] temp --- src/lib/XmlPatterns.php | 50 ----------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 src/lib/XmlPatterns.php diff --git a/src/lib/XmlPatterns.php b/src/lib/XmlPatterns.php deleted file mode 100644 index 86a4d1b..0000000 --- a/src/lib/XmlPatterns.php +++ /dev/null @@ -1,50 +0,0 @@ - - (?(DEFINE) - (? [:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]) - (? (?&NameStartChar) | [.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]) - (? (?&NameStartChar) (?&NameChar)*) - ) - ^(?&Name)$ - ~ux'; - } - - /** - * Get a regex pattern for valid tag chars - * - * @return string - */ - public static function getValidXmlTagNameChar() - { - return '~ - (?(DEFINE) - (? [:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]) - (? (?&NameStartChar) | [.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]) - ) - ^(?&NameChar)$ - ~ux'; - } - - /** - * Get a regex pattern for valid tag starting characters - * - * @return string - */ - public static function getValidXmlTagStartPattern() - { - return '~^([:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}])~ux'; - } -} \ No newline at end of file From fb0042403dc4e752a4c33095dcaafdb8171ace8a Mon Sep 17 00:00:00 2001 From: Roel Date: Sat, 21 Apr 2018 03:27:07 +0200 Subject: [PATCH 03/10] Add file again ( src/Lib/ ) --- src/Lib/XmlPatterns.php | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/Lib/XmlPatterns.php diff --git a/src/Lib/XmlPatterns.php b/src/Lib/XmlPatterns.php new file mode 100644 index 0000000..86a4d1b --- /dev/null +++ b/src/Lib/XmlPatterns.php @@ -0,0 +1,50 @@ + + (?(DEFINE) + (? [:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]) + (? (?&NameStartChar) | [.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]) + (? (?&NameStartChar) (?&NameChar)*) + ) + ^(?&Name)$ + ~ux'; + } + + /** + * Get a regex pattern for valid tag chars + * + * @return string + */ + public static function getValidXmlTagNameChar() + { + return '~ + (?(DEFINE) + (? [:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]) + (? (?&NameStartChar) | [.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]) + ) + ^(?&NameChar)$ + ~ux'; + } + + /** + * Get a regex pattern for valid tag starting characters + * + * @return string + */ + public static function getValidXmlTagStartPattern() + { + return '~^([:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}])~ux'; + } +} \ No newline at end of file From aced8a6019c050ab5c6db03d544b1fd4cf247ec8 Mon Sep 17 00:00:00 2001 From: Roel Date: Sat, 21 Apr 2018 14:36:17 +0200 Subject: [PATCH 04/10] Extract DomDocumentBuilder to its own class --- src/PhpArrayToXml.php | 255 +--------------------------- src/Traits/DomDocumentBuilder.php | 267 ++++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+), 251 deletions(-) create mode 100644 src/Traits/DomDocumentBuilder.php diff --git a/src/PhpArrayToXml.php b/src/PhpArrayToXml.php index 03c33d1..a56776d 100644 --- a/src/PhpArrayToXml.php +++ b/src/PhpArrayToXml.php @@ -2,12 +2,13 @@ namespace RefactorStudio\PhpArrayToXml; -use DOMDocument; -use DOMElement; use RefactorStudio\PhpArrayToXml\Lib\XmlPatterns; +use RefactorStudio\PhpArrayToXml\Traits\DomDocumentBuilder; class PhpArrayToXml { + use DomDocumentBuilder; + const LOWERCASE = 'lowercase'; const UPPERCASE = 'uppercase'; @@ -387,256 +388,8 @@ public static function isValidXmlTag($value = null) */ public function toXmlString($array = []) { - $this->_doc = new DOMDocument($this->getVersion(), $this->getEncoding()); - $this->_doc->formatOutput = $this->getFormatOutput(); - - $root = $this->_doc->createElement($this->createValidRootName($this->getCustomRootName())); - - $this->_doc->appendChild($root); - - $this->addArrayElements($root, $array); + $this->createDomDocument($array); return $this->_doc->saveXML(); } - - /** - * Converts arrays to DOMDocument elements - * - * @param DOMElement $parent - * @param array $array - */ - protected function addArrayElements(DOMElement $parent, $array = []) - { - if (is_array($array)) { - foreach ($array as $name => $value) { - if (!is_array($value)) { - // Create an XML element - $node = $this->createElement($name, $value); - $parent->appendChild($node); - } else { - - if (array_key_exists('@value', $value)) { - $cdata = array_key_exists('@cdata', $value) && $value['@cdata'] === true ? true : false; - $attributes = array_key_exists('@attr', $value) && is_array($value['@attr']) ? $value['@attr'] : []; - - if (!is_array($value['@value'])) { - // Create an XML element - $node = $this->createElement($name, $value['@value'], $cdata, $attributes); - $parent->appendChild($node); - } else { - // Create an empty XML element 'container' - $node = $this->createElement($name, null); - - foreach ($attributes as $attribute_name => $attribute_value) { - $node->setAttribute($attribute_name, $this->normalizeAttributeValue($attribute_value)); - } - - $parent->appendChild($node); - - // Add all the elements within the array to the 'container' - $this->addArrayElements($node, $value['@value']); - } - } else { - // Create an empty XML element 'container' - $node = $this->createElement($name, null); - $parent->appendChild($node); - - // Add all the elements within the array to the 'container' - $this->addArrayElements($node, $value); - } - } - } - } - } - - /** - * Normalize a value (replace some characters) - * - * @param $value - * @return null|string - */ - protected function normalizeValue($value) - { - if ($value === true) { - return $this->getCastBooleanValueTrue(); - } - - if ($value === false) { - return $this->getCastBooleanValueFalse(); - } - - if ($value === null) { - return $this->getCastNullValue(); - } - - return $value; - } - - /** - * Normalize an attribute value (replace some characters) - * - * @param $value - * @return string - */ - protected function normalizeAttributeValue($value) - { - if ($value === true) { - return 'true'; - } - - if ($value === false) { - return 'false'; - } - - return $value; - } - - /** - * See if a value matches an integer (could be a integer within a string) - * - * @param $value - * @return bool - */ - protected function isNumericKey($value) - { - $pattern = '~^(0|[1-9][0-9]*)$~ux'; - - return preg_match($pattern, $value) === 1; - } - - /** - * Creates an element for DOMDocument - * - * @param $name - * @param null|string $value - * @param bool $cdata - * @param array $attributes - * @return DOMElement - */ - protected function createElement($name, $value = null, $cdata = false, $attributes = []) - { - $name = $this->createValidTagName($name); - - if ($cdata === true) { - $element = $this->_doc->createElement($name); - $element->appendChild($this->_doc->createCDATASection($value)); - - foreach ($attributes as $attribute_name => $attribute_value) { - $element->setAttribute($attribute_name, $this->normalizeAttributeValue($attribute_value)); - } - - return $element; - } - - $element = $this->_doc->createElement($name, $this->normalizeValue($value)); - - foreach ($attributes as $attribute_name => $attribute_value) { - $element->setAttribute($attribute_name, $this->normalizeAttributeValue($attribute_value)); - } - - return $element; - } - - /** - * Creates a valid tag name - * - * @param null|string $name - * @return string - */ - protected function createValidTagName($name = null) - { - if (empty($name) || $this->isNumericKey($name)) { - $key = $name; - - if ($this->isValidXmlTag($this->getCustomTagName())) { - $name = $this->getCustomTagName(); - } else { - $name = $this->transformTagName($this->getDefaultTagName()); - } - - if ($this->getNumericTagSuffix() !== null) { - $name = $name.(string) $this->getNumericTagSuffix().$key; - } - return $name; - } - - if (!$this->isValidXmlTag($name)) { - $name = $this->replaceInvalidTagChars($name); - - if (!self::hasValidXmlTagStartingChar($name)) { - $name = $this->prefixInvalidTagStartingChar($name); - } - } - return $this->transformTagName($name); - } - - /** - * If a tag has an invalid starting character, use an underscore as prefix - * - * @param $value - * @return string - */ - protected function prefixInvalidTagStartingChar($value) - { - return '_'.substr($value, 1); - } - - /** - * Replace invalid tag characters - * - * @param $value - * @return null|string|string[] - */ - protected function replaceInvalidTagChars($value) - { - $pattern = ''; - for ($i = 0; $i < strlen($value); $i++) { - if (!self::isValidXmlTagChar($value[$i])) { - $pattern .= "\\$value[$i]"; - } - } - - if (!empty($pattern)) { - $value = preg_replace("/[{$pattern}]/", $this->getSeparator(), $value); - } - return $value; - } - - /** - * Creates a valid root name - * - * @param null|string $name - * @return string - */ - protected function createValidRootName($name = null) - { - if (is_string($name)) { - $name = preg_replace("/[^_a-zA-Z0-9]/", $this->getSeparator(), $name); - } - if ($this->isValidXmlTag($name)) { - return $name; - } - return $this->transformTagName($this->getDefaultRootName()); - } - - /** - * Transforms a tag name (only when specified) - * - * @param null|string $name - * @return null|string - */ - protected function transformTagName($name = null) - { - switch ($this->getTransformTags()) { - case self::LOWERCASE: { - return strtolower($name); - } - case self::UPPERCASE: { - return strtoupper($name); - } - default: { - return $name; - } - } - } } diff --git a/src/Traits/DomDocumentBuilder.php b/src/Traits/DomDocumentBuilder.php new file mode 100644 index 0000000..e85db9b --- /dev/null +++ b/src/Traits/DomDocumentBuilder.php @@ -0,0 +1,267 @@ +_doc = new DOMDocument($this->getVersion(), $this->getEncoding()); + $this->_doc->formatOutput = $this->getFormatOutput(); + + $root = $this->_doc->createElement($this->createValidRootName($this->getCustomRootName())); + + $this->_doc->appendChild($root); + + $this->addArrayElements($root, $array); + } + + /** + * Converts arrays to DOMDocument elements + * + * @param DOMElement $parent + * @param array $array + */ + protected function addArrayElements(DOMElement $parent, $array = []) + { + if (is_array($array)) { + foreach ($array as $name => $value) { + if (!is_array($value)) { + // Create an XML element + $node = $this->createXmlElement($name, $value); + $parent->appendChild($node); + } else { + + if (array_key_exists('@value', $value)) { + $cdata = array_key_exists('@cdata', $value) && $value['@cdata'] === true ? true : false; + $attributes = array_key_exists('@attr', $value) && is_array($value['@attr']) ? $value['@attr'] : []; + + if (!is_array($value['@value'])) { + // Create an XML element + $node = $this->createXmlElement($name, $value['@value'], $cdata, $attributes); + $parent->appendChild($node); + } else { + // Create an empty XML element 'container' + $node = $this->createXmlElement($name, null); + + foreach ($attributes as $attribute_name => $attribute_value) { + $node->setAttribute($attribute_name, $this->normalizeAttributeValue($attribute_value)); + } + + $parent->appendChild($node); + + // Add all the elements within the array to the 'container' + $this->addArrayElements($node, $value['@value']); + } + } else { + // Create an empty XML element 'container' + $node = $this->createXmlElement($name, null); + $parent->appendChild($node); + + // Add all the elements within the array to the 'container' + $this->addArrayElements($node, $value); + } + } + } + } + } + + /** + * Normalize a value (replace some characters) + * + * @param $value + * @return null|string + */ + protected function normalizeValue($value) + { + if ($value === true) { + return $this->getCastBooleanValueTrue(); + } + + if ($value === false) { + return $this->getCastBooleanValueFalse(); + } + + if ($value === null) { + return $this->getCastNullValue(); + } + + return $value; + } + + /** + * Normalize an attribute value (replace some characters) + * + * @param $value + * @return string + */ + protected function normalizeAttributeValue($value) + { + if ($value === true) { + return 'true'; + } + + if ($value === false) { + return 'false'; + } + + return $value; + } + + /** + * See if a value matches an integer (could be a integer within a string) + * + * @param $value + * @return bool + */ + protected function isNumericKey($value) + { + $pattern = '~^(0|[1-9][0-9]*)$~ux'; + + return preg_match($pattern, $value) === 1; + } + + /** + * Creates an element for DOMDocument + * + * @param $name + * @param null|string $value + * @param bool $cdata + * @param array $attributes + * @return DOMElement + */ + protected function createXmlElement($name, $value = null, $cdata = false, $attributes = []) + { + $name = $this->createValidTagName($name); + + if ($cdata === true) { + $element = $this->_doc->createElement($name); + $element->appendChild($this->_doc->createCDATASection($value)); + + foreach ($attributes as $attribute_name => $attribute_value) { + $element->setAttribute($attribute_name, $this->normalizeAttributeValue($attribute_value)); + } + + return $element; + } + + $element = $this->_doc->createElement($name, $this->normalizeValue($value)); + + foreach ($attributes as $attribute_name => $attribute_value) { + $element->setAttribute($attribute_name, $this->normalizeAttributeValue($attribute_value)); + } + + return $element; + } + + /** + * Creates a valid tag name + * + * @param null|string $name + * @return string + */ + protected function createValidTagName($name = null) + { + if (empty($name) || $this->isNumericKey($name)) { + $key = $name; + + if ($this->isValidXmlTag($this->getCustomTagName())) { + $name = $this->getCustomTagName(); + } else { + $name = $this->transformTagName($this->getDefaultTagName()); + } + + if ($this->getNumericTagSuffix() !== null) { + $name = $name.(string) $this->getNumericTagSuffix().$key; + } + return $name; + } + + if (!$this->isValidXmlTag($name)) { + $name = $this->replaceInvalidTagChars($name); + + if (!self::hasValidXmlTagStartingChar($name)) { + $name = $this->prefixInvalidTagStartingChar($name); + } + } + return $this->transformTagName($name); + } + + /** + * If a tag has an invalid starting character, use an underscore as prefix + * + * @param $value + * @return string + */ + protected function prefixInvalidTagStartingChar($value) + { + return '_'.substr($value, 1); + } + + /** + * Replace invalid tag characters + * + * @param $value + * @return null|string|string[] + */ + protected function replaceInvalidTagChars($value) + { + $pattern = ''; + for ($i = 0; $i < strlen($value); $i++) { + if (!self::isValidXmlTagChar($value[$i])) { + $pattern .= "\\$value[$i]"; + } + } + + if (!empty($pattern)) { + $value = preg_replace("/[{$pattern}]/", $this->getSeparator(), $value); + } + return $value; + } + + /** + * Creates a valid root name + * + * @param null|string $name + * @return string + */ + protected function createValidRootName($name = null) + { + if (is_string($name)) { + $name = preg_replace("/[^_a-zA-Z0-9]/", $this->getSeparator(), $name); + } + if ($this->isValidXmlTag($name)) { + return $name; + } + return $this->transformTagName($this->getDefaultRootName()); + } + + /** + * Transforms a tag name (only when specified) + * + * @param null|string $name + * @return null|string + */ + protected function transformTagName($name = null) + { + switch ($this->getTransformTags()) { + case self::LOWERCASE: { + return strtolower($name); + } + case self::UPPERCASE: { + return strtoupper($name); + } + default: { + return $name; + } + } + } +} \ No newline at end of file From 9e7afc021c34754020867820154629e05f2341f9 Mon Sep 17 00:00:00 2001 From: Roel Date: Sat, 21 Apr 2018 14:54:55 +0200 Subject: [PATCH 05/10] Cleaner boolean checking --- src/PhpArrayToXml.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpArrayToXml.php b/src/PhpArrayToXml.php index a56776d..528dd2c 100644 --- a/src/PhpArrayToXml.php +++ b/src/PhpArrayToXml.php @@ -255,7 +255,7 @@ public function setNumericTagSuffix($value = null) { $this->_numeric_tag_suffix = $value; - if ($value === true || $value === false) { + if(is_bool($value) === true) { $this->_numeric_tag_suffix = ''; } return $this; From 2e40dbccfed853cafd0dc4d35ea3872814ffb85d Mon Sep 17 00:00:00 2001 From: Roel Date: Sat, 21 Apr 2018 15:13:13 +0200 Subject: [PATCH 06/10] Apply Scrutinizer suggestions --- src/PhpArrayToXml.php | 21 ++++++++- src/Traits/DomDocumentBuilder.php | 77 +++++++++++++++++++------------ 2 files changed, 67 insertions(+), 31 deletions(-) diff --git a/src/PhpArrayToXml.php b/src/PhpArrayToXml.php index 528dd2c..4fc8f64 100644 --- a/src/PhpArrayToXml.php +++ b/src/PhpArrayToXml.php @@ -12,7 +12,6 @@ class PhpArrayToXml const LOWERCASE = 'lowercase'; const UPPERCASE = 'uppercase'; - protected $_doc; protected $_version = '1.0'; protected $_encoding = 'UTF-8'; protected $_default_root_name = 'root'; @@ -380,6 +379,26 @@ public static function isValidXmlTag($value = null) return false; } + /** + * Get the UPPERCASE constant + * + * @return string + */ + protected function getConstantUpperCase() + { + return self::UPPERCASE; + } + + /** + * Get the LOWERCASE constant + * + * @return string + */ + protected function getConstantLowerCase() + { + return self::LOWERCASE; + } + /** * Convert an array to XML * diff --git a/src/Traits/DomDocumentBuilder.php b/src/Traits/DomDocumentBuilder.php index e85db9b..afd27ea 100644 --- a/src/Traits/DomDocumentBuilder.php +++ b/src/Traits/DomDocumentBuilder.php @@ -7,6 +7,25 @@ trait DomDocumentBuilder { + protected $_doc; + + abstract public function getEncoding(); + abstract public function getVersion(); + abstract public function getFormatOutput(); + abstract public function getCustomRootName(); + abstract public function getCastBooleanValueTrue(); + abstract public function getCastBooleanValueFalse(); + abstract public function getCastNullValue(); + abstract public function getCustomTagName(); + abstract public function isValidXmlTag(); + abstract public function getDefaultTagName(); + abstract public function getNumericTagSuffix(); + abstract public function getSeparator(); + abstract public function getDefaultRootName(); + abstract public function getTransformTags(); + abstract protected function getConstantUpperCase(); + abstract protected function getConstantLowerCase(); + /** * Creates a DOMDocument from an array * @@ -32,43 +51,41 @@ protected function createDomDocument($array = []) */ protected function addArrayElements(DOMElement $parent, $array = []) { - if (is_array($array)) { - foreach ($array as $name => $value) { - if (!is_array($value)) { - // Create an XML element - $node = $this->createXmlElement($name, $value); - $parent->appendChild($node); - } else { - - if (array_key_exists('@value', $value)) { - $cdata = array_key_exists('@cdata', $value) && $value['@cdata'] === true ? true : false; - $attributes = array_key_exists('@attr', $value) && is_array($value['@attr']) ? $value['@attr'] : []; - - if (!is_array($value['@value'])) { - // Create an XML element - $node = $this->createXmlElement($name, $value['@value'], $cdata, $attributes); - $parent->appendChild($node); - } else { - // Create an empty XML element 'container' - $node = $this->createXmlElement($name, null); - - foreach ($attributes as $attribute_name => $attribute_value) { - $node->setAttribute($attribute_name, $this->normalizeAttributeValue($attribute_value)); - } + foreach ($array as $name => $value) { + if (!is_array($value)) { + // Create an XML element + $node = $this->createXmlElement($name, $value); + $parent->appendChild($node); + } else { - $parent->appendChild($node); + if (array_key_exists('@value', $value)) { + $cdata = array_key_exists('@cdata', $value) && $value['@cdata'] === true ? true : false; + $attributes = array_key_exists('@attr', $value) && is_array($value['@attr']) ? $value['@attr'] : []; - // Add all the elements within the array to the 'container' - $this->addArrayElements($node, $value['@value']); - } + if (!is_array($value['@value'])) { + // Create an XML element + $node = $this->createXmlElement($name, $value['@value'], $cdata, $attributes); + $parent->appendChild($node); } else { // Create an empty XML element 'container' $node = $this->createXmlElement($name, null); + + foreach ($attributes as $attribute_name => $attribute_value) { + $node->setAttribute($attribute_name, $this->normalizeAttributeValue($attribute_value)); + } + $parent->appendChild($node); // Add all the elements within the array to the 'container' - $this->addArrayElements($node, $value); + $this->addArrayElements($node, $value['@value']); } + } else { + // Create an empty XML element 'container' + $node = $this->createXmlElement($name, null); + $parent->appendChild($node); + + // Add all the elements within the array to the 'container' + $this->addArrayElements($node, $value); } } } @@ -253,10 +270,10 @@ protected function createValidRootName($name = null) protected function transformTagName($name = null) { switch ($this->getTransformTags()) { - case self::LOWERCASE: { + case $this->getConstantLowerCase(): { return strtolower($name); } - case self::UPPERCASE: { + case $this->getConstantUpperCase(): { return strtoupper($name); } default: { From 8ded7dfe3b4eb4a5d35d11eb13fb598710256b75 Mon Sep 17 00:00:00 2001 From: Roel Date: Sat, 21 Apr 2018 15:35:55 +0200 Subject: [PATCH 07/10] Apply Scrutinizer suggestions --- src/Traits/DomDocumentBuilder.php | 65 +++++++++++++++++++------------ 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/src/Traits/DomDocumentBuilder.php b/src/Traits/DomDocumentBuilder.php index afd27ea..3ca504f 100644 --- a/src/Traits/DomDocumentBuilder.php +++ b/src/Traits/DomDocumentBuilder.php @@ -17,7 +17,6 @@ abstract public function getCastBooleanValueTrue(); abstract public function getCastBooleanValueFalse(); abstract public function getCastNullValue(); abstract public function getCustomTagName(); - abstract public function isValidXmlTag(); abstract public function getDefaultTagName(); abstract public function getNumericTagSuffix(); abstract public function getSeparator(); @@ -25,6 +24,9 @@ abstract public function getDefaultRootName(); abstract public function getTransformTags(); abstract protected function getConstantUpperCase(); abstract protected function getConstantLowerCase(); + abstract public static function isValidXmlTag($value); + abstract public static function isValidXmlTagChar($value); + abstract public static function hasValidXmlTagStartingChar($value); /** * Creates a DOMDocument from an array @@ -40,7 +42,7 @@ protected function createDomDocument($array = []) $this->_doc->appendChild($root); - $this->addArrayElements($root, $array); + $this->createElementsFromArray($root, $array); } /** @@ -49,7 +51,7 @@ protected function createDomDocument($array = []) * @param DOMElement $parent * @param array $array */ - protected function addArrayElements(DOMElement $parent, $array = []) + protected function createElementsFromArray(DOMElement $parent, $array = []) { foreach ($array as $name => $value) { if (!is_array($value)) { @@ -57,40 +59,53 @@ protected function addArrayElements(DOMElement $parent, $array = []) $node = $this->createXmlElement($name, $value); $parent->appendChild($node); } else { - if (array_key_exists('@value', $value)) { - $cdata = array_key_exists('@cdata', $value) && $value['@cdata'] === true ? true : false; - $attributes = array_key_exists('@attr', $value) && is_array($value['@attr']) ? $value['@attr'] : []; - - if (!is_array($value['@value'])) { - // Create an XML element - $node = $this->createXmlElement($name, $value['@value'], $cdata, $attributes); - $parent->appendChild($node); - } else { - // Create an empty XML element 'container' - $node = $this->createXmlElement($name, null); - - foreach ($attributes as $attribute_name => $attribute_value) { - $node->setAttribute($attribute_name, $this->normalizeAttributeValue($attribute_value)); - } - - $parent->appendChild($node); - - // Add all the elements within the array to the 'container' - $this->addArrayElements($node, $value['@value']); - } + $this->createAdvancedXmlElement($parent, $value, $name); } else { // Create an empty XML element 'container' $node = $this->createXmlElement($name, null); $parent->appendChild($node); // Add all the elements within the array to the 'container' - $this->addArrayElements($node, $value); + $this->createElementsFromArray($node, $value); } } } } + /** + * Create an 'advanced' XML element, when the array has '@value' in it + * + * @param DOMElement $parent + * @param $value + * @param $name + * @return DOMElement + */ + protected function createAdvancedXmlElement(DOMElement $parent, $value, $name): DOMElement + { + $cdata = array_key_exists('@cdata', $value) && $value['@cdata'] === true ? true : false; + $attributes = array_key_exists('@attr', $value) && is_array($value['@attr']) ? $value['@attr'] : []; + + if (!is_array($value['@value'])) { + // Create an XML element + $node = $this->createXmlElement($name, $value['@value'], $cdata, $attributes); + + $parent->appendChild($node); + } else { + // Create an empty XML element 'container' + $node = $this->createXmlElement($name, null); + + foreach ($attributes as $attribute_name => $attribute_value) { + $node->setAttribute($attribute_name, $this->normalizeAttributeValue($attribute_value)); + } + $parent->appendChild($node); + + // Add all the elements within the array to the 'container' + $this->createElementsFromArray($node, $value['@value']); + } + return $node; + } + /** * Normalize a value (replace some characters) * From b2ee0f029ccb0787b1a69df336563df4536c44b9 Mon Sep 17 00:00:00 2001 From: Roel Date: Sat, 21 Apr 2018 15:39:09 +0200 Subject: [PATCH 08/10] scrutinizer spacing --- src/PhpArrayToXml.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpArrayToXml.php b/src/PhpArrayToXml.php index 4fc8f64..2b3a8a0 100644 --- a/src/PhpArrayToXml.php +++ b/src/PhpArrayToXml.php @@ -254,7 +254,7 @@ public function setNumericTagSuffix($value = null) { $this->_numeric_tag_suffix = $value; - if(is_bool($value) === true) { + if (is_bool($value) === true) { $this->_numeric_tag_suffix = ''; } return $this; From 2fc32d24a5e6c86de4c94bfdb73422a69aeda3ba Mon Sep 17 00:00:00 2001 From: Roel Date: Sat, 21 Apr 2018 15:40:37 +0200 Subject: [PATCH 09/10] Add argument --- src/PhpArrayToXml.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpArrayToXml.php b/src/PhpArrayToXml.php index 2b3a8a0..7df5598 100644 --- a/src/PhpArrayToXml.php +++ b/src/PhpArrayToXml.php @@ -247,7 +247,7 @@ public function getTransformTags() /** * Set the numeric tag suffix * - * @param null|string $value + * @param null|boolean|string $value * @return PhpArrayToXml */ public function setNumericTagSuffix($value = null) From b187cc3c541296bf5473fffe51946ac623636b4f Mon Sep 17 00:00:00 2001 From: Roel Date: Sat, 21 Apr 2018 15:51:23 +0200 Subject: [PATCH 10/10] Extract some methods --- src/Traits/DomDocumentBuilder.php | 55 ++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/src/Traits/DomDocumentBuilder.php b/src/Traits/DomDocumentBuilder.php index 3ca504f..d4d7378 100644 --- a/src/Traits/DomDocumentBuilder.php +++ b/src/Traits/DomDocumentBuilder.php @@ -203,28 +203,51 @@ protected function createXmlElement($name, $value = null, $cdata = false, $attri protected function createValidTagName($name = null) { if (empty($name) || $this->isNumericKey($name)) { - $key = $name; + return $this->createValidTagNameFromNumericValue($name); + } - if ($this->isValidXmlTag($this->getCustomTagName())) { - $name = $this->getCustomTagName(); - } else { - $name = $this->transformTagName($this->getDefaultTagName()); - } + if (!$this->isValidXmlTag($name)) { + $name = $this->makeTagNameValid($name); + } + return $this->transformTagName($name); + } - if ($this->getNumericTagSuffix() !== null) { - $name = $name.(string) $this->getNumericTagSuffix().$key; - } - return $name; + /** + * Make a tag name valid (replace invalid characters including starting characters) + * + * @param $name + * @return null|string|string[] + */ + protected function makeTagNameValid($name) + { + $name = $this->replaceInvalidTagChars($name); + + if (!self::hasValidXmlTagStartingChar($name)) { + $name = $this->prefixInvalidTagStartingChar($name); } + return $name; + } - if (!$this->isValidXmlTag($name)) { - $name = $this->replaceInvalidTagChars($name); + /** + * Create a valid tag name from a numeric value + * + * @param $name + * @return null|string + */ + protected function createValidTagNameFromNumericValue($name) + { + $key = $name; - if (!self::hasValidXmlTagStartingChar($name)) { - $name = $this->prefixInvalidTagStartingChar($name); - } + if ($this->isValidXmlTag($this->getCustomTagName())) { + $name = $this->getCustomTagName(); + } else { + $name = $this->transformTagName($this->getDefaultTagName()); } - return $this->transformTagName($name); + + if ($this->getNumericTagSuffix() !== null) { + $name = $name . (string)$this->getNumericTagSuffix() . $key; + } + return $name; } /**