Completed
Push — master ( f50da1...b30b9b )
by Dmitry
02:24
created

Space::getMapper()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Tarantool\Mapper;
4
5
use Exception;
6
7
class Space
8
{
9
    private $mapper;
10
11
    private $id;
12
    private $name;
13
    private $format;
14
    private $indexes;
15
16
    private $formatNamesHash = [];
17
    private $formatTypesHash = [];
18
19
    private $repository;
20
21
    public function __construct(Mapper $mapper, $id, $name)
22
    {
23
        $this->mapper = $mapper;
24
        $this->id = $id;
25
        $this->name = $name;
26
    }
27
28
    public function addProperties($config)
29
    {
30
        foreach ($config as $name => $type) {
31
            $this->addProperty($name, $type);
32
        }
33
        return $this;
34
    }
35
36
    public function addProperty($name, $type)
37
    {
38
        $format = $this->getFormat();
39
        foreach ($format as $field) {
40
            if ($field['name'] == $name) {
41
                throw new Exception("Property $name exists");
42
            }
43
        }
44
        $format[] = compact('name', 'type');
45
        $this->format = $format;
46
        $this->mapper->getClient()->evaluate("box.space[$this->id]:format(...)", [$format]);
47
48
        $this->parseFormat();
49
50
        return $this;
51
    }
52
53
    public function removeProperty($name)
54
    {
55
        $format = $this->getFormat();
56
        $last = array_pop($format);
57
        if ($last['name'] != $name) {
58
            throw new Exception("Remove only last property");
59
        }
60
        $this->mapper->getClient()->evaluate("box.space[$this->id]:format(...)", [$format]);
61
        $this->format = $format;
62
63
        $this->parseFormat();
64
65
        return $this;
66
    }
67
68
    public function removeIndex($name)
69
    {
70
        $this->mapper->getClient()->evaluate("box.space[$this->id].index.$name:drop()");
71
        $this->indexes = [];
72
        $this->mapper->getRepository('_index')->flushCache();
73
74
        return $this;
75
    }
76
77
    public function addIndex($config)
78
    {
79
        return $this->createIndex($config);
80
    }
81
82
    public function createIndex($config)
83
    {
84
        if (!is_array($config)) {
85
            $config = ['fields' => $config];
86
        }
87
88
89
        if (!array_key_exists('fields', $config)) {
90
            if (array_values($config) != $config) {
91
                throw new Exception("Invalid index configuration");
92
            }
93
            $config = [
94
                'fields' => $config
95
            ];
96
        }
97
98
        if (!is_array($config['fields'])) {
99
            $config['fields'] = [$config['fields']];
100
        }
101
102
        $options = [
103
            'parts' => []
104
        ];
105
106
        foreach ($config as $k => $v) {
107
            if ($k != 'name' && $k != 'fields') {
108
                $options[$k] = $v;
109
            }
110
        }
111
112
        foreach ($config['fields'] as $property) {
113
            if (!$this->getPropertyType($property)) {
114
                throw new Exception("Unknown property $property", 1);
115
            }
116
            $options['parts'][] = $this->getPropertyIndex($property)+1;
117
            $options['parts'][] = $this->getPropertyType($property);
118
        }
119
120
        $name = array_key_exists('name', $config) ? $config['name'] : implode('_', $config['fields']);
121
122
        $this->mapper->getClient()->evaluate("box.space[$this->id]:create_index('$name', ...)", [$options]);
123
        $this->indexes = [];
124
125
        $this->mapper->getSchema()->getSpace('_index')->getRepository()->flushCache();
126
127
        return $this;
128
    }
129
130
    public function isSpecial()
131
    {
132
        return $this->id == 280 || $this->id == 288;
133
    }
134
135
    public function getId()
136
    {
137
        return $this->id;
138
    }
139
140
    public function getTupleMap()
141
    {
142
        $reverse = [];
143
        foreach($this->getFormat() as $i => $field) {
144
            $reverse[$field['name']] = $i + 1;
145
        }
146
        return (object) $reverse;
147
    }
148
149
    public function getFormat()
150
    {
151
        if (!$this->format) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->format of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
152
            if ($this->isSpecial()) {
153
                $this->format = $this->mapper->getClient()
154
                    ->getSpace(280)->select([$this->id])->getData()[0][6];
155
            } else {
156
                $this->format = $this->mapper->findOne('_space', ['id' => $this->id])->format;
157
            }
158
            if (!$this->format) {
159
                $this->format = [];
160
            }
161
            $this->parseFormat();
162
        }
163
164
        return $this->format;
165
    }
166
167
    public function getMapper()
168
    {
169
        return $this->mapper;
170
    }
171
172
    public function getName()
173
    {
174
        return $this->name;
175
    }
176
177
    private function parseFormat()
178
    {
179
        $this->formatTypesHash = [];
180
        $this->formatNamesHash = [];
181
        foreach ($this->format as $key => $row) {
182
            $this->formatTypesHash[$row['name']] = $row['type'];
183
            $this->formatNamesHash[$row['name']] = $key;
184
        }
185
        return $this;
186
    }
187
188
    public function hasProperty($name)
189
    {
190
        $this->getFormat();
191
        return array_key_exists($name, $this->formatNamesHash);
192
    }
193
194
    public function getPropertyType($name)
195
    {
196
        if (!$this->hasProperty($name)) {
197
            throw new Exception("No property $name");
198
        }
199
        return $this->formatTypesHash[$name];
200
    }
201
202
    public function getPropertyIndex($name)
203
    {
204
        if (!$this->hasProperty($name)) {
205
            throw new Exception("No property $name");
206
        }
207
        return $this->formatNamesHash[$name];
208
    }
209
210
    public function getIndexes()
211
    {
212
        if (!$this->indexes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->indexes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
213
            if ($this->isSpecial()) {
214
                $this->indexes = [];
215
                $indexTuples = $this->mapper->getClient()->getSpace(288)->select([$this->id])->getData();
216
                $indexFormat = $this->mapper->getSchema()->getSpace(288)->getFormat();
217
                foreach ($indexTuples as $tuple) {
218
                    $instance = (object) [];
219
                    foreach ($indexFormat as $index => $format) {
220
                        $instance->{$format['name']} = $tuple[$index];
221
                    }
222
                    $this->indexes[] = $instance;
223
                }
224
            } else {
225
                $this->indexes = $this->mapper->find('_index', ['id' => $this->id]);
226
            }
227
        }
228
        return $this->indexes;
229
    }
230
231
    public function castIndex($params, $suppressException = false)
232
    {
233
        if(!count($this->getIndexes())) {
234
            return;
235
        }
236
        $keys = array_keys($params);
0 ignored issues
show
Unused Code introduced by
$keys is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
237
238
        $keys = [];
239
        foreach ($params as $name => $value) {
240
            $keys[] = $this->getPropertyIndex($name);
241
        }
242
        if ($keys == [0]) {
243
            // primary index
244
            return 0;
245
        }
246
247
        // equals
248
        foreach ($this->getIndexes() as $index) {
249
            $equals = false;
250
            if (count($keys) == count($index->parts)) {
251
                // same length
252
                $equals = true;
253
                foreach ($index->parts as $part) {
254
                    $equals = $equals && in_array($part[0], $keys);
255
                }
256
            }
257
258
            if ($equals) {
259
                return $index->iid;
260
            }
261
        }
262
263
        // index part
264
        foreach ($this->getIndexes() as $index) {
265
            $partial = [];
266
            foreach ($index->parts as $n => $part) {
267
                if (!array_key_exists($n, $keys)) {
268
                    break;
269
                }
270
                if ($keys[$n] != $part[0]) {
271
                    break;
272
                }
273
                $partial[] = $keys[$n];
274
            }
275
276
            if (count($partial) == count($keys)) {
277
                return $index->iid;
278
            }
279
        }
280
281
        if(!$suppressException) {
282
            throw new Exception("No index");
283
        }
284
    }
285
286
    public function getIndexValues($indexId, $params)
287
    {
288
        $index = null;
289
        foreach ($this->getIndexes() as $candidate) {
290
            if ($candidate->iid == $indexId) {
291
                $index = $candidate;
292
                break;
293
            }
294
        }
295
        if (!$index) {
296
            throw new Exception("Undefined index: $indexId");
297
        }
298
299
        $format = $this->getFormat();
300
        $values = [];
301
        foreach ($index->parts as $part) {
302
            $name = $format[$part[0]]['name'];
303
            if (!array_key_exists($name, $params)) {
304
                break;
305
            }
306
            $values[] = $this->mapper->getSchema()->formatValue($part[1], $params[$name]);
307
        }
308
        return $values;
309
    }
310
311
    public function getPrimaryIndex()
312
    {
313
        $indexes = $this->getIndexes();
314
        if (!count($indexes)) {
315
            throw new Exception("No primary index");
316
        }
317
        return $indexes[0];
318
    }
319
320
    public function getTupleKey($tuple)
321
    {
322
        $key = [];
323
        foreach ($this->getPrimaryIndex()->parts as $part) {
324
            $key[] = $tuple[$part[0]];
325
        }
326
        return count($key) == 1 ? $key[0] : implode(':', $key);
327
    }
328
329
    public function getInstanceKey($instance)
330
    {
331
        $key = [];
332
333
        foreach ($this->getPrimaryIndex()->parts as $part) {
334
            $name = $this->getFormat()[$part[0]]['name'];
335
            if (!property_exists($instance, $name)) {
336
                throw new Exception("Field $name is undefined", 1);
337
            }
338
            $key[] = $instance->$name;
339
        }
340
341
        return count($key) == 1 ? $key[0] : implode(':', $key);
342
    }
343
344
    public function getRepository()
345
    {
346
        $class = Repository::class;
347
        foreach ($this->mapper->getPlugins() as $plugin) {
348
            $repositoryClass = $plugin->getRepositoryClass($this);
349
            if ($repositoryClass) {
350
                if ($class != Repository::class) {
351
                    throw new Exception('Repository class override');
352
                }
353
                $class = $repositoryClass;
354
            }
355
        }
356
        return $this->repository ?: $this->repository = new $class($this);
357
    }
358
359
    public function repositoryExists()
360
    {
361
        return !!$this->repository;
362
    }
363
}
364