Comparator::instance()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 0
1
<?php
2
3
namespace ReenExe\CompareDataStructure;
4
5
class Comparator
6
{
7
    private $custom = [];
8
9
    /**
10
     * @var array
11
     */
12
    private $temporaryCustom;
13
14
    /**
15
     * @var array
16
     */
17
    private $exists;
18
19
    public static function addCustom(array $custom)
20
    {
21
        $instance = self::instance();
22
23
        $instance->custom = $instance->custom + $custom;
24
    }
25
26
    private function initialize(array $temporaryCustom)
27
    {
28
        $this->temporaryCustom = $this->custom + $temporaryCustom;
29
30
        $this->exists = array_keys($this->temporaryCustom);
31
    }
32
33
    /**
34
     * @param $data
35
     * @param $structure
36
     * @param array $custom
37
     * @return StructureDiffInfo
38
     */
39
    public static function check($data, $structure, array $custom = [])
40
    {
41
        $instance = self::instance();
42
43
        $instance->initialize($custom);
44
45
        return $instance->compare($data, $structure) ?: StructureDiffInfo::createEqual();
46
    }
47
48
    private function compare($data, $structure)
49
    {
50
        if (is_string($structure)) {
51
            return $this->diffType($data, $structure);
52
        }
53
54
        if (is_array($structure)) {
55
            return $this->diffStructure($data, $structure);
56
        }
57
58
        return $this->createDiff('undefined:structure', StructureDiffInfo::CONFIG);
59
    }
60
61
    /**
62
     * @param $value
63
     * @param array $types
64
     * @return StructureDiffInfo|null
65
     */
66
    private function diffTypes($value, array $types)
67
    {
68
        /**
69
         * Возможно тут будет переопределение проверки
70
         * К примеру формата даты или длины
71
         */
72
73
        if (in_array($this->getType($value), $types)) {
74
            return null;
75
        }
76
77
        if ($this->exists) {
78
            if ($intersect = array_intersect($types, $this->exists)) {
79
                foreach ($intersect as $key) {
80
                    $diff = $this->compare($value, $this->temporaryCustom[$key]);
81
82
                    if (empty($diff)) {
83
                        return null;
84
                    }
85
                }
86
87
                return $this->processDiff($diff, "custom:type:$key");
88
            }
89
        }
90
91
        return $this->createDiff('var:type', StructureDiffInfo::TYPE);
92
    }
93
94
    private function diffType($data, $structure)
95
    {
96
        $needTypes = explode('|', $structure);
97
98
        return $this->diffTypes($data, $needTypes);
99
    }
100
101
    /**
102
     * @param $data
103
     * @param $set
104
     * @return StructureDiffInfo|null
105
     */
106
    private function diffSet($data, $set)
107
    {
108
        $data = (array)$data;
109
        $set = (array)$set;
110
111
        if (array_diff($data, $set)) {
112
            return $this->createDiff('set:out', StructureDiffInfo::TYPE);
113
        }
114
115
        foreach ($data as $value) {
116
            if (!in_array($value, $set, true)) {
117
                return $this->createDiff('var:type', StructureDiffInfo::TYPE);
118
            }
119
        }
120
121
        return null;
122
    }
123
124
    /**
125
     * @param $data
126
     * @param array $structure
127
     * @return StructureDiffInfo|null
128
     */
129
    private function diffStructure($data, array $structure)
130
    {
131
        if (isset($structure['set'])) {
132
            return $this->diffSet($data, $structure['set']);
133
        }
134
135
        if ($diff = $this->diffTypes($data, $this->getStructureType($structure))) {
136
            return $diff;
137
        }
138
139
        if (is_array($data)) {
140
            return $this->diffArrayData($data, $structure);
141
        }
142
143
        return null;
144
    }
145
146
    /**
147
     * @param array $data
148
     * @param array $structure
149
     * @return StructureDiffInfo|null
150
     */
151
    private function diffArrayData(array $data, array $structure)
152
    {
153
        if (isset($structure['assoc'])) {
154
            return $this->assoc($structure['assoc'], $data);
155
        }
156
157
        if (isset($structure['values'])) {
158
            return $this->diffValuesStructure($data, $structure['values']);
159
        }
160
161
        return $this->createDiff('structure:type', StructureDiffInfo::CONFIG);
162
    }
163
164
    /**
165
     * @param array $data
166
     * @param $structure
167
     * @return StructureDiffInfo|null
168
     */
169
    private function diffValuesStructure(array $data, $structure)
170
    {
171
        if (is_array($structure)) {
172
            foreach ($data as $key => $subData) {
173
                if ($diff = $this->assoc($structure, $subData)) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $diff is correct as $this->assoc($structure, $subData) (which targets ReenExe\CompareDataStructure\Comparator::assoc()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
174
                    return $this->processDiff($diff, "[$key]");
175
                }
176
177
            }
178
        } elseif (is_string($structure)) {
179
            $needTypes = explode('|', $structure);
180
181
            foreach ($data as $key => $subData) {
182
                if ($diff = $this->diffTypes($subData, $needTypes)) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $diff is correct as $this->diffTypes($subData, $needTypes) (which targets ReenExe\CompareDataStruc...Comparator::diffTypes()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
183
                    return $this->processDiff($diff, "[$key]");
184
                }
185
            }
186
        }
187
188
        return null;
189
    }
190
191
    private function assoc(array $assoc, array $data)
192
    {
193
        foreach ($assoc as $key => $structure) {
194
            if (!array_key_exists($key, $data)) {
195
                return $this->createDiff($key, StructureDiffInfo::KEY);
196
            }
197
198
            if ($diff = $this->compare($data[$key], $structure)) {
199
                return $this->processDiff($diff, $key);
200
            }
201
        }
202
203
        return null;
204
    }
205
206
    private function getType($value)
207
    {
208
        return strtolower(gettype($value));
209
    }
210
211
    private function getStructureType(array $structure)
212
    {
213
        $types = ['array'];
214
215
        if (isset($structure['type'])) {
216
            $types = array_merge(
217
                $types,
218
                explode('|', $structure['type'])
219
            );
220
        }
221
222
        return $types;
223
    }
224
225
    private function createDiff($key, $message)
226
    {
227
        return $this->processDiff(StructureDiffInfo::createDiff($message), $key);
228
    }
229
230
    private function processDiff(StructureDiffInfo $diff, $key)
231
    {
232
        $diff->addPath($key);
233
234
        return $diff;
235
    }
236
237
    /**
238
     * @return Comparator
239
     */
240
    private static function instance()
241
    {
242
        static $self;
243
244
        return $self ?: $self = new self;
245
    }
246
}