Completed
Push — master ( b75a21...510a86 )
by Alexander
02:36
created

XmlConvertible::xmlIntersect()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Horat1us;
4
5
use Horat1us\Arrays\Collection;
6
use Horat1us\Services\XmlEqualityService;
7
use Horat1us\Services\XmlExportService;
8
use Horat1us\Services\XmlIntersectionService;
9
use Horat1us\Services\XmlParserService;
10
11
/**
12
 * Class XmlConvertible
13
 * @package Horat1us
14
 *
15
 * @mixin XmlConvertibleInterface
16
 */
17
trait XmlConvertible
18
{
19
    /**
20
     * @var XmlConvertibleInterface[]|\DOMNode[]|\DOMElement[]|null
21
     */
22
    public $xmlChildren;
23
24
    /**
25
     * Name of xml element (class name will be used by default)
26
     *
27
     * @var string
28
     */
29
    public $xmlElementName;
30
31
    /**
32
     * @param XmlConvertibleInterface $xml
33
     * @return XmlConvertible|XmlConvertibleInterface|null
34
     */
35 4
    public function xmlIntersect(
36
        XmlConvertibleInterface $xml
37
    )
38
    {
39 4
        $service = new XmlIntersectionService($this, $xml);
40 4
        return $service->intersect();
41
    }
42
43
    /**
44
     * @param XmlConvertibleInterface $xml
45
     * @return XmlConvertible|null
46
     */
47 4
    public function xmlDiff(XmlConvertibleInterface $xml)
48
    {
49 4
        $current = $this;
50 4
        $compared = $xml;
51
52
        if (
53 4
            $current->getXmlElementName() !== $compared->getXmlElementName()
54 4
            || empty($current->getXmlChildren()) && !empty($compared->getXmlChildren())
55 3
            || array_reduce(
56 3
                $current->getXmlProperties(),
57
                function (bool $carry, string $property) use ($compared, $current) : bool {
58 3
                    return $carry
59 3
                        || (!property_exists($compared, $property))
60 3
                        || $current->{$property} !== $compared->{$property};
61 3
                },
62 4
                false
63
            )
64
        ) {
65 4
            return clone $current;
66
        }
67
68
69 2
        $newChildren = Collection::from($current->getXmlChildren() ?? [])
70
            ->map(function ($child) use ($compared) {
71 2
                return array_reduce(
72 2
                    $compared->getXmlChildren() ?? [],
73
                    function ($carry, $comparedChild) use ($child) {
74 2
                        if ($carry) {
75
                            return $carry;
76
                        }
77
78 2
                        $diff = ($child instanceof XmlConvertibleInterface
79 2
                            ? $child
80
                            : XmlConvertibleObject::fromXml($child)
81 2
                        )->xmlDiff(
82
                            $comparedChild instanceof XmlConvertibleInterface
83 2
                                ? $comparedChild
84 2
                                : XmlConvertibleObject::fromXml($comparedChild)
85
                        );
86
87 2
                        return $diff;
88 2
                    });
89 2
            })
90
            ->filter(function ($child) {
91 2
                return $child !== null;
92 2
            })
93 2
            ->array;
94
95 2
        if (empty($newChildren)) {
96 2
            return null;
97
        }
98
99 2
        $target = clone $current;
100 2
        $target->setXmlChildren($newChildren);
101
102 2
        return clone $target;
103
    }
104
105
    /**
106
     * Converts object to XML and compares it with given
107
     *
108
     * @param XmlConvertibleInterface $xml
109
     * @return bool
110
     */
111 3
    public function xmlEqual(XmlConvertibleInterface $xml): bool
112
    {
113 3
        $service = new XmlEqualityService($this, $xml);
114 3
        return $service->compare();
115
    }
116
117
    /**
118
     * @param \DOMDocument|\DOMElement $document
119
     * @param array $aliases
120
     * @return XmlConvertibleInterface
121
     */
122 12
    public static function fromXml($document, array $aliases = [])
123
    {
124
        /** @var \DOMElement $document */
125 12
        if (!in_array(get_called_class(), $aliases)) {
126 7
            $aliases[(new \ReflectionClass(get_called_class()))->getShortName()] = get_called_class();
127
        }
128 12
        return (new XmlParserService($document, $aliases))->convert();
129
    }
130
131
    /**
132
     * @param \DOMDocument|null $document
133
     * @return \DOMElement
134
     */
135 14
    public function toXml(\DOMDocument $document = null): \DOMElement
136
    {
137 14
        $service = new XmlExportService($this, $document);
138 14
        return $service->export();
139
    }
140
141
    /**
142
     * Name of xml element (class name will be used by default)
143
     *
144
     * @return string
145
     */
146 23
    public function getXmlElementName(): string
147
    {
148 23
        return $this->xmlElementName ?? (new \ReflectionClass(get_called_class()))->getShortName();
149
    }
150
151
    /**
152
     * Settings name of xml element
153
     *
154
     * @param string $name
155
     * @return static
156
     */
157 1
    public function setXmlElementName(string $name = null)
158
    {
159 1
        $this->xmlElementName = $name;
160 1
        return $this;
161
    }
162
163
    /**
164
     * @return XmlConvertibleInterface[]|\DOMNode[]|\DOMElement[]|null
165
     */
166 23
    public function getXmlChildren()
167
    {
168 23
        return $this->xmlChildren;
169
    }
170
171
    /**
172
     * @param XmlConvertibleInterface[]|\DOMNode[]|\DOMElement[]|null $xmlChildren
173
     * @return static
174
     */
175 14
    public function setXmlChildren(array $xmlChildren = null)
176
    {
177 14
        $this->xmlChildren = $xmlChildren ?: null;
178 14
        return $this;
179
    }
180
181
    /**
182
     * Getting array of property names which will be used as attributes in created XML
183
     *
184
     * @param array|null $properties
185
     * @return array|string[]
186
     */
187 23
    public function getXmlProperties(array $properties = null): array
188
    {
189 23
        $properties = $properties
190
            ?: array_map(function(\ReflectionProperty $property) {
191 18
                return $property->getName();
192 23
            }, (new \ReflectionClass(get_called_class()))->getProperties(\ReflectionProperty::IS_PUBLIC));
193
194
195
        return array_filter($properties, function(string $property) {
196 23
            return !in_array($property, ['xmlChildren', 'xmlElementName']);
197 23
        });
198
    }
199
200
    /**
201
     * Cloning all children by default
202
     */
203
    public function __clone()
204
    {
205 19
        $this->xmlChildren = array_map(function ($xmlChild) {
206 5
            return clone $xmlChild;
207 19
        }, $this->xmlChildren ?? []) ?: null;
208
    }
209
}