Completed
Push — master ( 8784bb...001bdf )
by Reen
02:34
created

Comparator::diffStructure()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 17
rs 9.2
cc 4
eloc 7
nc 4
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");
1 ignored issue
show
Bug introduced by
The variable $key seems to be defined by a foreach iteration on line 72. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
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
            if (is_array($structure['values'])) {
134
                foreach ($data as $key => $subData) {
135
136
                    if ($diff = $this->assoc($structure['values'], $subData)) {
137
                        return $this->processDiff($diff, "[$key]");
138
                    }
139
140
                }
141
            } elseif (is_string($structure['values'])) {
142
                $needTypes = explode('|', $structure['values']);
143
144
                foreach ($data as $key => $subData) {
145
146
                    if ($diff = $this->checkTypes($subData, $needTypes)) {
147
                        return $this->processDiff($diff, "[$key]");
148
                    }
149
                }
150
            }
151
        } else {
152
            return $this->createDiff('structure:type', StructureDiffInfo::CONFIG);
153
        }
154
    }
155
156
    private function assoc(array $assoc, array $data)
157
    {
158
        foreach ($assoc as $key => $structure) {
159
160
            if (!array_key_exists($key, $data)) {
161
                return $this->createDiff($key, StructureDiffInfo::KEY);
162
            };
163
164
            if ($diff = $this->compare($data[$key], $structure)) {
165
                return $this->processDiff($diff, $key);
166
            }
167
        }
168
    }
169
170
    private function getType($value)
171
    {
172
        return strtolower(gettype($value));
173
    }
174
175
    private function getStructureType(array $structure)
176
    {
177
        $types = ['array'];
178
179
        if (isset($structure['type'])) {
180
            $types = array_merge(
181
                $types,
182
                explode('|', $structure['type'])
183
            );
184
        }
185
186
        return $types;
187
    }
188
189
    private function createDiff($key, $message)
190
    {
191
        return $this->processDiff(StructureDiffInfo::createDiff($message), $key);
192
    }
193
194
    private function processDiff(StructureDiffInfo $diff, $key)
195
    {
196
        $diff->addPath($key);
197
198
        return $diff;
199
    }
200
201
    /**
202
     * @return Comparator
203
     */
204
    private static function instance()
205
    {
206
        static $self;
207
208
        return $self ?: $self = new self;
209
    }
210
}