Completed
Push — master ( 312add...3463fb )
by Dmitry
03:07
created

Schema   F

Complexity

Total Complexity 60

Size/Duplication

Total Lines 216
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 5

Importance

Changes 0
Metric Value
wmc 60
lcom 3
cbo 5
dl 0
loc 216
rs 3.6
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
B createSpace() 0 32 6
C getDefaultValue() 0 24 14
C formatValue() 0 29 15
A getSpace() 0 18 5
A getSpaceId() 0 7 2
A getSpaces() 0 7 2
A hasSpace() 0 4 1
A once() 0 10 2
A reset() 0 11 2
A getMeta() 0 13 2
A setMeta() 0 6 1
A toUnderscore() 0 12 4
A toCamelCase() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like Schema often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Schema, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Tarantool\Mapper;
4
5
use Exception;
6
7
class Schema
8
{
9
    private $mapper;
10
11
    private $names = [];
12
    private $spaces = [];
13
    private $params = [];
14
    private $engines = [];
15
16
    public function __construct(Mapper $mapper, $meta = null)
17
    {
18
        $this->mapper = $mapper;
19
        if ($meta) {
20
            $this->setMeta($meta);
21
        } else {
22
            $this->reset();
23
        }
24
    }
25
26
    public function createSpace($space, $config = [])
27
    {
28
        $engine = 'memtx';
29
        if (array_key_exists('properties', $config)) {
30
            if (array_key_exists('engine', $config)) {
31
                $engine = $config['engine'];
32
                if (!in_array($engine, ['memtx', 'vinyl'])) {
33
                    throw new Exception("Invalid engine $engine");
34
                }
35
            }
36
        }
37
38
        $id = $this->mapper->getClient()->evaluate("
39
            box.schema.space.create('$space', {
40
                engine = '$engine'
41
            })
42
            return box.space.$space.id
43
        ")->getData()[0];
44
45
        $this->names[$space] = $id;
46
        $this->engines[$space] = $engine;
47
48
        $this->spaces[$id] = new Space($this->mapper, $id, $space, $engine);
49
50
        $properties = array_key_exists('properties', $config) ? $config['properties'] : $config;
51
52
        if ($properties) {
53
            $this->spaces[$id]->addProperties($properties);
54
        }
55
56
        return $this->spaces[$id];
57
    }
58
59
    public function getDefaultValue($type)
60
    {
61
        switch ($type) {
62
            case 'STR':
63
            case 'STRING':
64
            case 'str':
65
            case 'string':
66
                return (string) null;
67
68
            case 'double':
69
            case 'float':
70
            case 'number':
71
                return (float) null;
72
73
            case 'integer':
74
            case 'INTEGER':
75
            case 'unsigned':
76
            case 'UNSIGNED':
77
            case 'num':
78
            case 'NUM':
79
                return (int) null;
80
        }
81
        throw new Exception("Invalid type $type");
82
    }
83
84
    public function formatValue($type, $value)
85
    {
86
        if (is_null($value)) {
87
            return null;
88
        }
89
        switch ($type) {
90
            case 'STR':
91
            case 'STRING':
92
            case 'str':
93
            case 'string':
94
                return (string) $value;
95
96
            case 'double':
97
            case 'float':
98
            case 'number':
99
                return (float) $value;
100
101
            case 'integer':
102
            case 'INTEGER':
103
            case 'unsigned':
104
            case 'UNSIGNED':
105
            case 'num':
106
            case 'NUM':
107
                return (int) $value;
108
109
            default:
110
                return $value;
111
        }
112
    }
113
114
    public function getSpace($id)
115
    {
116
        if (is_string($id)) {
117
            return $this->getSpace($this->getSpaceId($id));
118
        }
119
120
        if (!$id) {
121
            throw new Exception("Space id or name not defined");
122
        }
123
124
        if (!array_key_exists($id, $this->spaces)) {
125
            $name = array_search($id, $this->names);
126
            $meta = array_key_exists($id, $this->params) ? $this->params[$id] : null;
127
            $engine = $this->engines[$name];
128
            $this->spaces[$id] = new Space($this->mapper, $id, $name, $engine, $meta);
129
        }
130
        return $this->spaces[$id];
131
    }
132
133
    public function getSpaceId($name)
134
    {
135
        if (!$this->hasSpace($name)) {
136
            throw new Exception("No space $name");
137
        }
138
        return $this->names[$name];
139
    }
140
141
    public function getSpaces()
142
    {
143
        foreach ($this->names as $id) {
144
            $this->getSpace($id);
145
        }
146
        return $this->spaces;
147
    }
148
149
    public function hasSpace($name)
150
    {
151
        return array_key_exists($name, $this->names);
152
    }
153
154
    public function once($name, $callback)
155
    {
156
        $key = 'mapper-once' . $name;
157
158
        $rows = $this->mapper->find('_schema', ['key' => $key]);
159
        if (!count($rows)) {
160
            $this->mapper->create('_schema', ['key' => $key]);
161
            return $callback($this->mapper);
162
        }
163
    }
164
165
    public function reset()
166
    {
167
        $this->names = [];
168
        $this->engines = [];
169
170
        $data = $this->mapper->getClient()->getSpace('_vspace')->select()->getData();
171
        foreach ($data as $tuple) {
0 ignored issues
show
Bug introduced by
The expression $data of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
172
            $this->names[$tuple[2]] = $tuple[0];
173
            $this->engines[$tuple[2]] = $tuple[3];
174
        }
175
    }
176
177
    public function getMeta()
178
    {
179
        $params = [];
180
        foreach ($this->getSpaces() as $space) {
181
            $params[$space->getId()] = $space->getMeta();
182
        }
183
184
        return [
185
            'engines' => $this->engines,
186
            'names' => $this->names,
187
            'params' => $params,
188
        ];
189
    }
190
191
    public function setMeta($meta)
192
    {
193
        $this->engines = $meta['engines'];
194
        $this->names = $meta['names'];
195
        $this->params = $meta['params'];
196
    }
197
198
    private $underscores = [];
199
200
    public function toUnderscore($input)
201
    {
202
        if (!array_key_exists($input, $this->underscores)) {
203
            preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
204
            $ret = $matches[0];
205
            foreach ($ret as &$match) {
206
                $match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
207
            }
208
            $this->underscores[$input] = implode('_', $ret);
209
        }
210
        return $this->underscores[$input];
211
    }
212
213
    private $camelcase = [];
214
215
    public function toCamelCase($input)
216
    {
217
        if (!array_key_exists($input, $this->camelcase)) {
218
            $this->camelcase[$input] = lcfirst(implode('', array_map('ucfirst', explode('_', $input))));
219
        }
220
        return $this->camelcase[$input];
221
    }
222
}
223