Completed
Push — master ( 001bdf...40bb6e )
by Reen
02:01
created

Comparator::diffArrayData()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 12
rs 9.4285
cc 3
eloc 6
nc 3
nop 2
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
    private function checkTypes($value, array $types)
62
    {
63
        /**
64
         * Возможно тут будет переопределение проверки
65
         * К примеру формата даты или длины
66
         */
67
68
        if (in_array($this->getType($value), $types)) return; /* success */
69
70
        if ($this->exists) {
71
            if ($intersect = array_intersect($types, $this->exists)) {
72
                foreach ($intersect as $key) {
73
                    $diff = $this->compare($value, $this->temporaryCustom[$key]);
74
75
                    if (empty($diff)) return; /* success */
76
                }
77
78
                return $this->processDiff($diff, "custom:type:$key");
79
            }
80
        }
81
82
        return $this->createDiff('var:type', StructureDiffInfo::TYPE);
83
    }
84
85
    private function diffType($data, $structure)
86
    {
87
        $needTypes = explode('|', $structure);
88
89
        return $this->checkTypes($data, $needTypes);
90
    }
91
92
    private function diffSet($data, $set)
93
    {
94
        $data = (array)$data;
95
        $set = (array)$set;
96
97
        if (array_diff($data, $set)) {
98
            return $this->createDiff('set:out', StructureDiffInfo::TYPE);
99
        }
100
101
        foreach ($data as $value) {
102
            if (in_array($value, $set, true)) continue;
103
104
            return $this->createDiff('var:type', StructureDiffInfo::TYPE);
105
        }
106
    }
107
108
    private function diffStructure($data, array $structure)
109
    {
110
        /**
111
         * structure `set`
112
         */
113
        if (isset($structure['set'])) {
114
            return $this->diffSet($data, $structure['set']);
115
        }
116
117
        if ($diff = $this->checkTypes($data, $this->getStructureType($structure))) {
118
            return $diff;
119
        }
120
121
        if (is_array($data)) {
122
            return $this->diffArrayData($data, $structure);
123
        }
124
    }
125
126
    private function diffArrayData(array $data, array $structure)
127
    {
128
        if (isset($structure['assoc'])) {
129
            return $this->assoc($structure['assoc'], $data);
130
        }
131
132
        if (isset($structure['values'])) {
133
            return $this->diffValuesStructure($data, $structure);
134
        }
135
136
        return $this->createDiff('structure:type', StructureDiffInfo::CONFIG);
137
    }
138
139
    private function diffValuesStructure(array $data, array $structure)
140
    {
141
        if (is_array($structure['values'])) {
142
            foreach ($data as $key => $subData) {
143
                if ($diff = $this->assoc($structure['values'], $subData)) {
144
                    return $this->processDiff($diff, "[$key]");
145
                }
146
147
            }
148
        } elseif (is_string($structure['values'])) {
149
            $needTypes = explode('|', $structure['values']);
150
151
            foreach ($data as $key => $subData) {
152
                if ($diff = $this->checkTypes($subData, $needTypes)) {
153
                    return $this->processDiff($diff, "[$key]");
154
                }
155
            }
156
        }
157
    }
158
159
    private function assoc(array $assoc, array $data)
160
    {
161
        foreach ($assoc as $key => $structure) {
162
            if (!array_key_exists($key, $data)) {
163
                return $this->createDiff($key, StructureDiffInfo::KEY);
164
            };
165
166
            if ($diff = $this->compare($data[$key], $structure)) {
167
                return $this->processDiff($diff, $key);
168
            }
169
        }
170
    }
171
172
    private function getType($value)
173
    {
174
        return strtolower(gettype($value));
175
    }
176
177
    private function getStructureType(array $structure)
178
    {
179
        $types = ['array'];
180
181
        if (isset($structure['type'])) {
182
            $types = array_merge(
183
                $types,
184
                explode('|', $structure['type'])
185
            );
186
        }
187
188
        return $types;
189
    }
190
191
    private function createDiff($key, $message)
192
    {
193
        return $this->processDiff(StructureDiffInfo::createDiff($message), $key);
194
    }
195
196
    private function processDiff(StructureDiffInfo $diff, $key)
197
    {
198
        $diff->addPath($key);
199
200
        return $diff;
201
    }
202
203
    /**
204
     * @return Comparator
205
     */
206
    private static function instance()
207
    {
208
        static $self;
209
210
        return $self ?: $self = new self;
211
    }
212
}