diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..9fada8f
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,41 @@
+name: Integrity check
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@master
+
+ - name: Install PHP
+ uses: shivammathur/setup-php@master
+ with:
+ php-version: 7.4
+
+ - name: Install composer deps
+ run: |
+ composer create-project nette/code-checker temp/code-checker ^3 --no-progress
+ composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress
+
+ # Install app deps
+ composer install --no-interaction --prefer-dist
+
+ # Check code checker and coding standards
+ - name: Check coding standards
+ run: |
+ php temp/code-checker/code-checker --short-arrays --strict-types --fix --no-progress
+ php temp/coding-standard/ecs check src --config temp/coding-standard/coding-standard-php71.yml
+
+ - name: Check PHPStan rules
+ run: composer phpstan
+
+ - name: Run tests
+ run: tests/test.sh
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..2c31e5c
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <>
\ No newline at end of file
diff --git a/README.md b/README.md
index 8c5d61d..4e76274 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,26 @@
+XML to PHP array convertor
+==========================
+
+Smart tool to convert your XML to PHP array.
+
+Install and simply use
+----------------------
+
+Use Composer:
+
+```shell
+composer require gaarf/xml-to-php-array
+```
+
+And then package will be automatically installed to your project and you can simply call:
+
+```php
+$resultArray = Convertor::covertToArray($xml);
+```
+
+Documentation
+-------------
+
One common need when working in PHP is a way to convert an XML document
into a serializable array. If you ever tried to serialize() and then
unserialize() a SimpleXML or DOMDocument object, you know what I’m
@@ -5,74 +28,82 @@ talking about.
Assume the following XML snippet:
-
-
- Brian
- Chris
- Meg
-
-
+```xml
+
+
+ Brian
+ Chris
+ Meg
+
+
+```
There’s a quick and dirty way to do convert such a document to an array,
using type casting and the JSON functions to ensure there are no exotic
values that would cause problems when unserializing:
-
+```php
+$a = json_decode(json_encode((array) Convertor::covertToArray($s)), true);
+```
Here is the result for our sample XML, eg if we `print_r($a)`:
- Array
- (
- [show] => Array
- (
- [@attributes] => Array
- (
- [name] => Family Guy
- )
- [dog] => Brian
- [kid] => Array
- (
- [0] => Chris
- [1] => Meg
- )
- )
- )
+```
+Array
+(
+ [show] => Array
+ (
+ [@attributes] => Array
+ (
+ [name] => Family Guy
+ )
+ [dog] => Brian
+ [kid] => Array
+ (
+ [0] => Chris
+ [1] => Meg
+ )
+ )
+)
+```
Pretty nifty, eh? But maybe we want to embed some HTML tags or something
crazy along those lines. then we need a CDATA node…
-
-
- Brian
- Chris
- Meg
- Stewie]]>
-
-
+```xml
+
+
+ Brian
+ Chris
+ Meg
+ Stewie]]>
+
+
+```
The snippet of XML above would yield the following:
- Array
- (
- [show] => Array
- (
- [@attributes] => Array
- (
- [name] => Family Guy
- )
- [dog] => Brian
- [kid] => Array
- (
- [0] => Chris
- [1] => Meg
- [2] => Array
- (
- )
- )
- )
- )
+```
+Array
+(
+ [show] => Array
+ (
+ [@attributes] => Array
+ (
+ [name] => Family Guy
+ )
+ [dog] => Brian
+ [kid] => Array
+ (
+ [0] => Chris
+ [1] => Meg
+ [2] => Array
+ (
+ )
+ )
+ )
+)
+```
That’s not very useful. We got in trouble because the CDATA node, a
SimpleXMLElement, is being cast to an array instead of a string. To
@@ -82,27 +113,38 @@ hereby released under a do-whatever-but-dont-sue-me license.
The result, for our *Stewie* snippet:
- Array
- (
- [show] => Array
- (
- [@attributes] => Array
- (
- [name] => Family Guy
- )
- [dog] => Brian
- [kid] => Array
- (
- [0] => Chris
- [1] => Meg
- [2] => Stewie
- )
- )
- )
+```
+Array
+(
+ [show] => Array
+ (
+ [@attributes] => Array
+ (
+ [name] => Family Guy
+ )
+ [dog] => Brian
+ [kid] => Array
+ (
+ [0] => Chris
+ [1] => Meg
+ [2] => Stewie
+ )
+ )
+)
+```
Victory is mine! :D
---
### Contributions
+
[clh-code#1] If a node has attributes, but contains only text, then the output will be an array with both ```@content``` and ```@attributes``` keys
+
+[reggi#4] store root element tag name in ```@root```
+
+[janbarasek#13] Add support for PHP 7.1 + better code style.
+
+[janbarasek#15] Rewrite repository as Composer package.
+
+[roland-d#18] Treat empty node as string
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..d6d3a20
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,35 @@
+{
+ "name": "gaarf/xml-to-php-array",
+ "description": "XML to PHP array convertor",
+ "homepage": "https://github.com/gaarf/XML-string-to-PHP-array",
+ "authors": [
+ {
+ "name": "Adrien Cahen",
+ "homepage": "http://gaarf.info"
+ },
+ {
+ "name": "Jan Barášek",
+ "homepage": "https://baraja.cz"
+ }
+ ],
+ "require": {
+ "php": ">=7.1.0",
+ "ext-dom": "*"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^0.12.18",
+ "tracy/tracy": "^2.7",
+ "phpstan/phpstan-nette": "^0.12.6"
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpstan": [
+ "vendor/bin/phpstan analyse src -c phpstan.neon --level 6 --no-progress"
+ ]
+ },
+ "minimum-stability": "stable"
+}
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..25bf1ec
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,3 @@
+includes:
+ - vendor/phpstan/phpstan-nette/extension.neon
+ - vendor/phpstan/phpstan-nette/rules.neon
diff --git a/src/Convertor.php b/src/Convertor.php
new file mode 100644
index 0000000..300a360
--- /dev/null
+++ b/src/Convertor.php
@@ -0,0 +1,31 @@
+loadXML($xml);
+ $root = $doc->documentElement;
+ $output = (array) Helper::domNodeToArray($root);
+ $output['@root'] = $root->tagName;
+
+ return $output ?? [];
+ }
+}
\ No newline at end of file
diff --git a/src/Helper.php b/src/Helper.php
new file mode 100644
index 0000000..a4acbe4
--- /dev/null
+++ b/src/Helper.php
@@ -0,0 +1,69 @@
+nodeType) {
+ case 4: // XML_CDATA_SECTION_NODE
+ case 3: // XML_TEXT_NODE
+ $output = trim($node->textContent);
+ break;
+ case 1: // XML_ELEMENT_NODE
+ for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++) {
+ $child = $node->childNodes->item($i);
+ $v = self::domNodeToArray($child);
+ if (isset($child->tagName)) {
+ $t = $child->tagName;
+ if (!isset($output[$t])) {
+ $output[$t] = [];
+ }
+ if (is_array($v) && empty($v)) {
+ $v = '';
+ }
+ $output[$t][] = $v;
+ } elseif ($v || $v === '0') {
+ $output = (string) $v;
+ }
+ }
+ if ($node->attributes->length && !is_array($output)) { // has attributes but isn't an array
+ $output = ['@content' => $output]; // change output into an array.
+ }
+ if (is_array($output)) {
+ if ($node->attributes->length) {
+ $a = [];
+ foreach ($node->attributes as $attrName => $attrNode) {
+ $a[$attrName] = (string) $attrNode->value;
+ }
+ $output['@attributes'] = $a;
+ }
+ foreach ($output as $t => $v) {
+ if ($t !== '@attributes' && is_array($v) && count($v) === 1) {
+ $output[$t] = $v[0];
+ }
+ }
+ }
+ break;
+ }
+
+ return $output;
+ }
+}
diff --git a/test.sh b/tests/test.sh
similarity index 74%
rename from test.sh
rename to tests/test.sh
index 02dc458..663964b 100755
--- a/test.sh
+++ b/tests/test.sh
@@ -1,7 +1,8 @@
#!/usr/bin/env php
+
Brian
Chris
@@ -34,6 +35,7 @@ $xmlstr = << array(
array(
@@ -62,7 +64,7 @@ $expected = array(
),
array(
- "empty" => array(),
+ "empty" => "",
"foo" => array(
"@attributes" => array(
"empty" => ""
@@ -76,13 +78,22 @@ $expected = array(
)
)
- )
-);
+ ),
-$result = xmlstr_to_array($xmlstr);
+ "@attributes" => array( "type" => "cartoon" ),
+ "@root" => 'tv',
+);
-prettyPrint("Input", $xmlstr);
-prettyPrint("Expected", $expected);
-prettyPrint("Output", $result);
-prettyPrint("Result", $result == $expected ? "SUCCESS :-)" : "FAILURE :-(");
+$result = \Gaarf\XmlToPhp\Convertor::covertToArray($xmlstr);
+
+if ($result == $expected) {
+ prettyPrint('Result', 'SUCCESS :-)');
+} else {
+ prettyPrint('Result', 'FAILURE :-(');
+ prettyPrint('Input', $xmlstr);
+ prettyPrint('Expected', $expected);
+ prettyPrint('Output', $result);
+ prettyPrint('Result', 'FAILURE :-(');
+ exit(1);
+}
diff --git a/xmlstr_to_array.php b/xmlstr_to_array.php
deleted file mode 100644
index c847de2..0000000
--- a/xmlstr_to_array.php
+++ /dev/null
@@ -1,63 +0,0 @@
-loadXML($xmlstr);
- return domnode_to_array($doc->documentElement);
-}
-
-function domnode_to_array($node) {
- $output = array();
- switch ($node->nodeType) {
-
- case XML_CDATA_SECTION_NODE:
- case XML_TEXT_NODE:
- $output = trim($node->textContent);
- break;
-
- case XML_ELEMENT_NODE:
- for ($i=0, $m=$node->childNodes->length; $i<$m; $i++) {
- $child = $node->childNodes->item($i);
- $v = domnode_to_array($child);
- if(isset($child->tagName)) {
- $t = $child->tagName;
- if(!isset($output[$t])) {
- $output[$t] = array();
- }
- $output[$t][] = $v;
- }
- elseif($v || $v === '0') {
- $output = (string) $v;
- }
- }
- if($node->attributes->length && !is_array($output)) { //Has attributes but isn't an array
- $output = array('@content'=>$output); //Change output into an array.
- }
- if(is_array($output)) {
- if($node->attributes->length) {
- $a = array();
- foreach($node->attributes as $attrName => $attrNode) {
- $a[$attrName] = (string) $attrNode->value;
- }
- $output['@attributes'] = $a;
- }
- foreach ($output as $t => $v) {
- if(is_array($v) && count($v)==1 && $t!='@attributes') {
- $output[$t] = $v[0];
- }
- }
- }
- break;
- }
- return $output;
-}
-?>
\ No newline at end of file