XML::toArray()   D
last analyzed

Complexity

Conditions 16
Paths 106

Size

Total Lines 93
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 34
c 0
b 0
f 0
nc 106
nop 2
dl 0
loc 93
rs 4.774

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Koch Framework
5
 * Jens-André Koch © 2005 - onwards.
6
 *
7
 * This file is part of "Koch Framework".
8
 *
9
 * License: GNU/GPL v2 or any later version, see LICENSE file.
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 2 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace Koch\Datatype;
26
27
/**
28
 * Koch Framework - Class for handling and converting XML data.
29
 *
30
 * This class converts XML-based data into JSON or Array formatted data.
31
 */
32
class XML
0 ignored issues
show
Coding Style introduced by
XML does not seem to conform to the naming convention (Utils?$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
33
{
34
    const MAX_RECURSION_DEPTH_ALLOWED = 25;
35
    const FETCH_ATTRIBUTES            = true;
36
    const REMOVE_ATTRIBUTES_SUBLEVEL  = true;
37
38
    /**
39
     * toJson.
40
     *
41
     * This function transforms the XML based String data into JSON format. If the input XML
42
     * string is in table format, the resulting JSON output will also be in table format.
43
     * Conversely, if the input XML string is in tree format, the resulting JSON output will
44
     * also be in tree format.
45
     *
46
     * @param string XML data string.
47
     *
48
     * @return false|string a string containing JSON table/tree formatted data. Otherwise, it returns an empty string.
49
     */
50
    public static function toJson($xml)
51
    {
52
        if ($xml === null) {
53
            return false;
54
        }
55
56
        $xml = simplexml_load_string($xml);
57
58
        $json = '';
59
60
        // convert the XML structure into PHP array structure.
61
        $array = self::toArray($xml);
62
63
        if (($array !== null) && (sizeof($array) > 0)) {
64
            $json = json_encode($array);
65
        }
66
67
        return $json;
68
    }
69
70
    /**
71
     * toArray.
72
     *
73
     * This function accepts a SimpleXmlElementObject as a single argument
74
     * and converts it into a PHP associative array.
75
     * If the input XML is in table format (i.e. non-nested), the resulting associative
76
     * array will also be in a table format. Conversely, if the input XML is in
77
     * tree (i.e. nested) format, this function will return an associative array
78
     * (tree/nested) representation of that XML.
79
     *
80
     * @param object Simple XML Element Object
81
     * @param int Recursion Depth
82
     *
83
     * @return array Returns assoc array containing the data from XML. Otherwise, returns null.
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
84
     */
85
    public static function toArray($xml, $recursionDepth = 0)
86
    {
87
        // Keep an eye on how deeply we are involved in recursion.
88
        if ($recursionDepth > self::MAX_RECURSION_DEPTH_ALLOWED) {
89
            return;
90
        }
91
92
        // we are at top/root level
93
        if ($recursionDepth === 0) {
94
            // If the external caller doesn't call this function initially
95
            // with a SimpleXMLElement object just return
96
            if (get_class($xml) !== 'SimpleXMLElement') {
97
                return;
98
            } else { // store original SimpleXmlElementObject sent by the caller.
99
                $provided_xml_object = $xml;
0 ignored issues
show
Coding Style introduced by
$provided_xml_object does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
100
            }
101
        }
102
103
        if (is_a($xml, 'SimpleXMLElement')) {
104
            // Get a copy of the simpleXmlElementObject
105
            $copy_of_xml_object = $xml;
0 ignored issues
show
Coding Style introduced by
$copy_of_xml_object does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
106
            // Get the object variables in the SimpleXmlElement object for us to iterate.
107
            $xml = get_object_vars($xml);
108
        }
109
110
        // It needs to be an array of object variables.
111
        if (is_array($xml)) {
112
            // Initialize the result array.
113
            $resultArray = [];
114
115
            // Is the input array size 0? Then, we reached the rare CDATA text if any.
116
            if (count($xml) === 0) {
117
                // return the lonely CDATA. It could even be whitespaces.
118
                return (string) $copy_of_xml_object;
0 ignored issues
show
Coding Style introduced by
$copy_of_xml_object does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Bug introduced by
The variable $copy_of_xml_object does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
119
            }
120
121
            // walk through the child elements now.
122
            foreach ($xml as $key => $value) {
123
                /*
124
                 * Add Attributes to the results array ?
125
                 */
126
                if (self::FETCH_ATTRIBUTES === false) {
127
                    // check KEY - is it an attribute?
128
                    if ((is_string($key)) && ($key === '@attributes')) {
129
                        // yes, don't add, just continue with next element of foreach
130
                        continue;
131
                    }
132
                }
133
134
                // increase the recursion depth by one.
135
                ++$recursionDepth;
136
137
                // WATCH IT ! RECURSION !!!
138
                // recursively process the current (VALUE) element
139
                $resultArray[$key] = self::toArray($value, $recursionDepth);
140
141
                // decrease the recursion depth by one
142
                --$recursionDepth;
143
            }
144
145
            // we are reaching the top/root level
146
            if ($recursionDepth === 0) {
147
                /*
148
                 * Set the XML root element name as the root [top-level] key of
149
                 * the associative array that we are going to return to the caller of this
150
                 * recursive function.
151
                 */
152
                $tempArray                                    = $resultArray;
153
                $resultArray                                  = [];
154
                $resultArray[$provided_xml_object->getName()] = $tempArray;
0 ignored issues
show
Coding Style introduced by
$provided_xml_object does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Bug introduced by
The variable $provided_xml_object does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
155
            }
156
157
            /*
158
             * Shifts every key/value pair of @attributes one level up and removes @attributes
159
             */
160
            if (self::FETCH_ATTRIBUTES && self::REMOVE_ATTRIBUTES_SUBLEVEL === true) {
161
                // ok, add attributes
162
                if (isset($resultArray['@attributes'])) {
163
                    // but as key => value elements
164
                    foreach ($resultArray['@attributes'] as $key => $value) {
165
                        $resultArray[$key] = $value;
166
                    }
167
                    // removing @attributes
168
                    unset($resultArray['@attributes']);
169
                }
170
            }
171
172
            return $resultArray;
173
        } else {
174
            // it's is either the XML attribute text or text between XML tags
175
            return (string) $xml;
176
        }
177
    }
178
}
179