XML   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 0
loc 240
rs 10
c 0
b 0
f 0
wmc 24
lcom 1
cbo 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A __toString() 0 4 1
A getData() 0 4 1
A setData() 0 5 1
A getXML() 0 4 1
A setXML() 0 5 1
A toArray() 0 19 2
A toXML() 0 9 1
A parse() 0 6 1
A createXML() 0 4 1
A stripXMLWhitespace() 0 9 1
C childNodesToArray() 0 45 7
B arrayPropertiesToXmlNodes() 0 28 5
1
<?php declare(strict_types=1);
2
/**
3
 * Anime List Client
4
 *
5
 * An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
6
 *
7
 * PHP version 7
8
 *
9
 * @package     AnimeListClient
10
 * @author      Timothy J. Warren <[email protected]>
11
 * @copyright   2015 - 2017  Timothy J. Warren
12
 * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
13
 * @version     4.0
14
 * @link        https://github.com/timw4mail/HummingBirdAnimeClient
15
 */
16
17
namespace Aviat\AnimeClient\API;
18
19
use DOMDocument, DOMNode, DOMNodelist;
20
21
/**
22
 * XML <=> PHP Array codec
23
 */
24
class XML {
25
26
	/**
27
	 * XML representation of the data
28
	 *
29
	 * @var string
30
	 */
31
	private $xml;
32
33
	/**
34
	 * PHP array version of the data
35
	 *
36
	 * @var array
37
	 */
38
	private $data;
39
40
	/**
41
	 * XML constructor
42
	 */
43
	public function __construct(string $xml = '', array $data = [])
44
	{
45
		$this->setXML($xml)->setData($data);
46
	}
47
48
	/**
49
	 * Serialize the data to an xml string
50
	 */
51
	public function __toString(): string
52
	{
53
		return static::toXML($this->getData());
54
	}
55
56
	/**
57
	 * Get the data parsed from the XML
58
	 *
59
	 * @return array
60
	 */
61
	public function getData(): array
62
	{
63
		return $this->data;
64
	}
65
66
	/**
67
	 * Set the data to create xml from
68
	 *
69
	 * @param array $data
70
	 * @return $this
71
	 */
72
	public function setData(array $data): self
73
	{
74
		$this->data = $data;
75
		return $this;
76
	}
77
78
	/**
79
	 * Get the xml created from the data
80
	 *
81
	 * @return string
82
	 */
83
	public function getXML(): string
84
	{
85
		return $this->xml;
86
	}
87
88
	/**
89
	 * Set the xml to parse the data from
90
	 *
91
	 * @param string $xml
92
	 * @return $this
93
	 */
94
	public function setXML(string $xml): self
95
	{
96
		$this->xml = $xml;
97
		return $this;
98
	}
99
100
	/**
101
	 * Parse an xml document string to a php array
102
	 *
103
	 * @param string $xml
104
	 * @return array
105
	 */
106
	public static function toArray(string $xml): array
107
	{
108
		$data = [];
109
110
		$xml = static::stripXMLWhitespace($xml);
0 ignored issues
show
Bug introduced by
Since stripXMLWhitespace() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of stripXMLWhitespace() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
111
112
		$dom = new DOMDocument();
113
		$dom->loadXML($xml);
114
		$root = $dom->documentElement;
115
116
		$data[$root->tagName] = [];
117
118
		if ($root->hasChildNodes())
119
		{
120
			static::childNodesToArray($data[$root->tagName], $root->childNodes);
0 ignored issues
show
Bug introduced by
Since childNodesToArray() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of childNodesToArray() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
121
		}
122
123
		return $data;
124
	}
125
126
	/**
127
	 * Transform the array into XML
128
	 *
129
	 * @param array $data
130
	 * @return string
131
	 */
132
	public static function toXML(array $data): string
133
	{
134
		$dom = new DOMDocument();
135
		$dom->encoding = 'UTF-8';
136
137
		static::arrayPropertiesToXmlNodes($dom, $dom, $data);
0 ignored issues
show
Bug introduced by
Since arrayPropertiesToXmlNodes() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of arrayPropertiesToXmlNodes() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
138
139
		return $dom->saveXML();
140
	}
141
142
	/**
143
	 * Parse the xml document string to a php array
144
	 *
145
	 * @return array
146
	 */
147
	public function parse(): array
148
	{
149
		$xml = $this->getXML();
150
		$data = static::toArray($xml);
151
		return $this->setData($data)->getData();
152
	}
153
154
	/**
155
	 * Transform the array into XML
156
	 *
157
	 * @return string
158
	 */
159
	public function createXML(): string
160
	{
161
		return static::toXML($this->getData());
162
	}
163
164
	private static function stripXMLWhitespace(string $xml): string
165
	{
166
		// Get rid of unimportant text nodes by removing
167
		// whitespace characters from between xml tags,
168
		// except for the xml declaration tag, Which looks
169
		// something like:
170
		/* <?xml version="1.0" encoding="UTF-8"?> */
171
		return preg_replace('/([^\?])>\s+</', '$1><', $xml);
172
	}
173
174
	/**
175
	 * Recursively create array structure based on xml structure
176
	 *
177
	 * @param array &$root A reference to the current array location
178
	 * @param DOMNodeList $nodeList The current NodeList object
0 ignored issues
show
Documentation introduced by
Should the type for parameter $nodeList not be \DOMNodeList?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
179
	 * @return void
180
	 */
181
	private static function childNodesToArray(array &$root, DOMNodelist $nodeList)
182
	{
183
		$length = $nodeList->length;
184
		for ($i = 0; $i < $length; $i++)
185
		{
186
			$el = $nodeList->item($i);
187
			$current =& $root[$el->nodeName];
188
189
			// It's a top level element!
190
			if (is_a($el->childNodes->item(0), 'DomText') || ( ! $el->hasChildNodes()))
191
			{
192
				$current = $el->textContent;
193
				continue;
194
			}
195
196
			// An empty value at the current root
197
			if (is_null($current))
198
			{
199
				$current = [];
200
				static::childNodesToArray($current, $el->childNodes);
0 ignored issues
show
Bug introduced by
Since childNodesToArray() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of childNodesToArray() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
201
				continue;
202
			}
203
204
			$keys = array_keys($current);
205
206
			// Wrap the array in a containing array
207
			// if there are only string keys
208
			if ( ! is_numeric($keys[0]))
209
			{
210
				// But if there is only one key, don't wrap it in
211
				// an array, just recurse to parse the child nodes
212
				if (count($current) === 1)
213
				{
214
					static::childNodesToArray($current, $el->childNodes);
0 ignored issues
show
Bug introduced by
Since childNodesToArray() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of childNodesToArray() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
215
					continue;
216
				}
217
				$current = [$current];
218
			}
219
220
			array_push($current, []);
221
			$index = count($current) - 1;
222
223
			static::childNodesToArray($current[$index], $el->childNodes);
0 ignored issues
show
Bug introduced by
Since childNodesToArray() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of childNodesToArray() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
224
		}
225
	}
226
227
	/**
228
	 * Recursively create xml nodes from array properties
229
	 *
230
	 * @param DOMDocument $dom The current DOM object
231
	 * @param DOMNode $parent The parent element to append children to
232
	 * @param array $data The data for the current node
233
	 * @return void
234
	 */
235
	private static function arrayPropertiesToXmlNodes(DOMDocument &$dom, DOMNode &$parent, array $data)
236
	{
237
		foreach($data as $key => $props)
238
		{
239
			// 'Flatten' the array as you create the xml
240
			if (is_numeric($key))
241
			{
242
				foreach($props as $key => $props)
243
				{
244
					break;
245
				}
246
			}
247
248
			$node = $dom->createElement($key);
249
250
			if (is_array($props))
251
			{
252
				static::arrayPropertiesToXmlNodes($dom, $node, $props);
0 ignored issues
show
Bug introduced by
Since arrayPropertiesToXmlNodes() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of arrayPropertiesToXmlNodes() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
253
			}
254
			else
255
			{
256
				$tNode = $dom->createTextNode((string)$props);
257
				$node->appendChild($tNode);
258
			}
259
260
			$parent->appendChild($node);
261
		}
262
	}
263
}