Horat1us /
php-xml-convertible
| 1 | <?php |
||||
| 2 | |||||
| 3 | namespace Horat1us\Services; |
||||
| 4 | |||||
| 5 | use Horat1us\XmlConvertibleInterface; |
||||
| 6 | use Horat1us\XmlConvertibleObject; |
||||
| 7 | |||||
| 8 | /** |
||||
| 9 | * Class XmlParserService |
||||
| 10 | * @package Horat1us\Services |
||||
| 11 | */ |
||||
| 12 | class XmlParserService |
||||
| 13 | { |
||||
| 14 | /** |
||||
| 15 | * @var \DOMNode |
||||
| 16 | */ |
||||
| 17 | protected $element; |
||||
| 18 | |||||
| 19 | /** |
||||
| 20 | * @var XmlConvertibleInterface[] |
||||
| 21 | */ |
||||
| 22 | protected $aliases; |
||||
| 23 | |||||
| 24 | /** |
||||
| 25 | * XmlParserService constructor. |
||||
| 26 | * @param $document |
||||
| 27 | * @param array $aliases |
||||
| 28 | */ |
||||
| 29 | public function __construct($document, array $aliases = []) |
||||
| 30 | { |
||||
| 31 | 15 | $this |
|||
| 32 | ->setAliases($aliases) |
||||
| 33 | ->setDocument($document); |
||||
| 34 | 15 | } |
|||
| 35 | 12 | ||||
| 36 | 11 | public function __clone() |
|||
| 37 | { |
||||
| 38 | 8 | $this->element = clone $this->element; |
|||
| 39 | } |
||||
| 40 | 8 | ||||
| 41 | 8 | /** |
|||
| 42 | * @return XmlConvertibleInterface |
||||
| 43 | */ |
||||
| 44 | public function convert() |
||||
| 45 | { |
||||
| 46 | 10 | $nodeObject = new XmlConvertibleObject($this->element->nodeName); |
|||
| 47 | foreach ($this->aliases as $key => $alias) { |
||||
| 48 | if ($this->getIsAliasMatch($key, $alias)) { |
||||
| 49 | 10 | $nodeObject = clone $alias; |
|||
| 50 | } |
||||
| 51 | 10 | } |
|||
| 52 | 10 | ||||
| 53 | if ($this->element->hasChildNodes()) { |
||||
| 54 | 10 | $this->convertChildren($nodeObject); |
|||
| 55 | 8 | } |
|||
| 56 | if ($this->element->hasAttributes()) { |
||||
| 57 | 10 | $this->convertAttributes($nodeObject); |
|||
| 58 | 9 | } |
|||
| 59 | |||||
| 60 | return $nodeObject; |
||||
| 61 | 10 | } |
|||
| 62 | |||||
| 63 | /** |
||||
| 64 | * @param XmlConvertibleInterface $object |
||||
| 65 | * @return $this |
||||
| 66 | */ |
||||
| 67 | public function convertChildren(XmlConvertibleInterface &$object) |
||||
| 68 | 8 | { |
|||
| 69 | $children = []; |
||||
| 70 | 8 | $service = clone $this; |
|||
| 71 | 8 | ||||
| 72 | foreach ($this->element->childNodes as $childNode) { |
||||
| 73 | 8 | $service->setDocument($childNode); |
|||
| 74 | 8 | $children[] = $service->convert(); |
|||
| 75 | 8 | } |
|||
| 76 | $object->setXmlChildren($children); |
||||
| 77 | 8 | ||||
| 78 | return $this; |
||||
| 79 | 8 | } |
|||
| 80 | |||||
| 81 | public function convertAttributes(XmlConvertibleInterface &$object) |
||||
| 82 | 9 | { |
|||
| 83 | $properties = $object->getXmlProperties(); |
||||
| 84 | 9 | /** @var \DOMAttr $attribute */ |
|||
| 85 | foreach ($this->element->attributes as $attribute) { |
||||
| 86 | 9 | if ( |
|||
| 87 | !$object instanceof XmlConvertibleObject |
||||
| 88 | 9 | && !in_array($attribute->name, $properties) |
|||
| 89 | 9 | ) { |
|||
| 90 | throw new \UnexpectedValueException( |
||||
| 91 | 1 | get_class($object) . ' must have defined ' . $attribute->name . ' XML property', |
|||
| 92 | 1 | 4 |
|||
| 93 | 1 | ); |
|||
| 94 | } |
||||
| 95 | $object->{$attribute->name} = $attribute->value; |
||||
| 96 | 9 | } |
|||
| 97 | } |
||||
| 98 | 9 | ||||
| 99 | /** |
||||
| 100 | * @param \DOMNode|\DOMDocument $document |
||||
| 101 | * @return $this |
||||
| 102 | */ |
||||
| 103 | public function setDocument($document) |
||||
| 104 | 12 | { |
|||
| 105 | if ($document instanceof \DOMDocument) { |
||||
| 106 | 12 | return $this->setDocument($document->firstChild); |
|||
| 107 | 8 | } |
|||
| 108 | |||||
| 109 | if (!$document instanceof \DOMNode) { |
||||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||||
| 110 | 12 | throw new \InvalidArgumentException("Document must be instance of DOMElement or DOMNode"); |
|||
| 111 | 1 | } |
|||
| 112 | $this->element = $document; |
||||
| 113 | 11 | ||||
| 114 | return $this; |
||||
| 115 | 11 | } |
|||
| 116 | |||||
| 117 | /** |
||||
| 118 | * @return \DOMElement |
||||
| 119 | */ |
||||
| 120 | public function getDocument() |
||||
| 121 | 1 | { |
|||
| 122 | return $this->element; |
||||
| 123 | 1 | } |
|||
| 124 | |||||
| 125 | /** |
||||
| 126 | * @param XmlConvertibleInterface[]|string $aliases |
||||
| 127 | * @return $this |
||||
| 128 | */ |
||||
| 129 | public function setAliases(array $aliases = []) |
||||
| 130 | 15 | { |
|||
| 131 | $this->aliases = []; |
||||
| 132 | 15 | ||||
| 133 | foreach ($aliases as $key => $element) { |
||||
| 134 | 15 | $element = $this->mapAlias($element); |
|||
| 135 | $this->aliases[$this->mapKey($key, $element)] = $element; |
||||
| 136 | 13 | } |
|||
| 137 | 15 | ||||
| 138 | return $this; |
||||
| 139 | 10 | } |
|||
| 140 | 12 | ||||
| 141 | /** |
||||
| 142 | 12 | * @return XmlConvertibleInterface[] |
|||
| 143 | */ |
||||
| 144 | public function getAliases() |
||||
| 145 | { |
||||
| 146 | return $this->aliases; |
||||
| 147 | } |
||||
| 148 | 1 | ||||
| 149 | /** |
||||
| 150 | 1 | * @param string|XmlConvertibleInterface $element |
|||
| 151 | * @return XmlConvertibleInterface |
||||
| 152 | */ |
||||
| 153 | protected function mapAlias($element) |
||||
| 154 | { |
||||
| 155 | if ($element instanceof XmlConvertibleInterface) { |
||||
| 156 | return $element; |
||||
| 157 | 13 | } |
|||
| 158 | if (!is_string($element)) { |
||||
|
0 ignored issues
–
show
|
|||||
| 159 | 13 | throw new \UnexpectedValueException( |
|||
| 160 | 10 | "All aliases must be instance or class implements " . XmlConvertibleInterface::class |
|||
| 161 | ); |
||||
| 162 | 12 | } |
|||
| 163 | 3 | ||||
| 164 | 3 | // Additional type-checking |
|||
| 165 | return $this->mapAlias(new $element()); |
||||
| 166 | } |
||||
| 167 | |||||
| 168 | /** |
||||
| 169 | 10 | * @param string|integer $key |
|||
| 170 | * @param XmlConvertibleInterface $element |
||||
| 171 | * @return string |
||||
| 172 | */ |
||||
| 173 | protected function mapKey($key, XmlConvertibleInterface $element): string |
||||
| 174 | { |
||||
| 175 | return is_numeric($key) |
||||
| 176 | ? $element->getXmlElementName() |
||||
| 177 | 10 | : $key; |
|||
| 178 | } |
||||
| 179 | 10 | ||||
| 180 | 1 | /** |
|||
| 181 | 10 | * @param string $key |
|||
| 182 | * @param XmlConvertibleInterface $element |
||||
| 183 | * @return bool |
||||
| 184 | */ |
||||
| 185 | protected function getIsAliasMatch(string $key, XmlConvertibleInterface $element): bool |
||||
| 186 | { |
||||
| 187 | if ($this->element->nodeName !== $key) { |
||||
| 188 | return false; |
||||
| 189 | 10 | } |
|||
| 190 | |||||
| 191 | 10 | return array_reduce( |
|||
| 192 | 7 | array_filter( |
|||
| 193 | $element->getXmlProperties(), |
||||
| 194 | function ($property) use ($element) { |
||||
| 195 | 9 | return empty($element->{$property}); |
|||
| 196 | } |
||||
| 197 | 6 | ), |
|||
| 198 | 9 | function (bool $carry, $property) use ($element) { |
|||
| 199 | 9 | return $carry && $this->element->attributes->getNamedItem($property) != $element->{$property}; |
|||
|
0 ignored issues
–
show
The method
getNamedItem() does not exist on null.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||||
| 200 | 6 | }, |
|||
| 201 | 9 | true |
|||
| 202 | ); |
||||
| 203 | } |
||||
| 204 | } |
||||
| 205 |