Failed Conditions
Pull Request — 3.0.x (#3980)
by Guilherme
14:00 queued 29s
created

Index   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 338
Duplicated Lines 0 %

Test Coverage

Coverage 97.87%

Importance

Changes 0
Metric Value
eloc 75
dl 0
loc 338
ccs 92
cts 94
cp 0.9787
rs 8.48
c 0
b 0
f 0
wmc 49

21 Methods

Rating   Name   Duplication   Size   Complexity  
A hasFlag() 0 3 1
A getOptions() 0 3 1
A getQuotedColumns() 0 20 5
A _addColumn() 0 3 1
A hasColumnAtPosition() 0 6 1
A spansColumns() 0 15 4
A getOption() 0 3 1
A addFlag() 0 5 1
A getUnquotedColumns() 0 3 1
A isUnique() 0 3 1
A isPrimary() 0 3 1
A __construct() 0 16 4
A samePartialIndex() 0 7 5
B isFullfilledBy() 0 36 8
A getColumns() 0 3 1
A getFlags() 0 3 1
A isSimpleIndex() 0 3 2
A hasSameColumnLengths() 0 8 1
A hasOption() 0 3 1
B overrules() 0 11 7
A removeFlag() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Index 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.

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 Index, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Doctrine\DBAL\Schema;
4
5
use Doctrine\DBAL\Platforms\AbstractPlatform;
6
use InvalidArgumentException;
7
use function array_filter;
8
use function array_keys;
9
use function array_map;
10
use function array_search;
11
use function array_shift;
12
use function count;
13
use function strtolower;
14
15
class Index extends AbstractAsset implements Constraint
16
{
17
    /**
18
     * Asset identifier instances of the column names the index is associated with.
19
     * array($columnName => Identifier)
20
     *
21
     * @var Identifier[]
22
     */
23
    protected $_columns = [];
24
25
    /** @var bool */
26
    protected $_isUnique = false;
27
28
    /** @var bool */
29
    protected $_isPrimary = false;
30
31
    /**
32
     * Platform specific flags for indexes.
33
     * array($flagName => true)
34
     *
35
     * @var true[]
36
     */
37
    protected $_flags = [];
38
39
    /**
40
     * Platform specific options
41
     *
42
     * @todo $_flags should eventually be refactored into options
43
     * @var mixed[]
44
     */
45
    private $options = [];
46
47
    /**
48
     * @param string   $indexName
49
     * @param string[] $columns
50
     * @param bool     $isUnique
51
     * @param bool     $isPrimary
52
     * @param string[] $flags
53
     * @param mixed[]  $options
54
     */
55 10280
    public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = [], array $options = [])
56
    {
57 10280
        $isUnique = $isUnique || $isPrimary;
58
59 10280
        $this->_setName($indexName);
60
61 10280
        $this->_isUnique  = $isUnique;
62 10280
        $this->_isPrimary = $isPrimary;
63 10280
        $this->options    = $options;
64
65 10280
        foreach ($columns as $column) {
66 10170
            $this->_addColumn($column);
67
        }
68
69 10280
        foreach ($flags as $flag) {
70 192
            $this->addFlag($flag);
71
        }
72 10280
    }
73
74
    /**
75
     * @return void
76
     *
77
     * @throws InvalidArgumentException
78
     */
79 10170
    protected function _addColumn(string $column)
80
    {
81 10170
        $this->_columns[$column] = new Identifier($column);
82 10170
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87 8216
    public function getColumns()
88
    {
89 8216
        return array_keys($this->_columns);
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95 6488
    public function getQuotedColumns(AbstractPlatform $platform)
96
    {
97 6488
        $subParts = $platform->supportsColumnLengthIndexes() && $this->hasOption('lengths')
98 6488
            ? $this->getOption('lengths') : [];
99
100 6488
        $columns = [];
101
102 6488
        foreach ($this->_columns as $column) {
103 6466
            $length = array_shift($subParts);
104
105 6466
            $quotedColumn = $column->getQuotedName($platform);
106
107 6466
            if ($length !== null) {
108 24
                $quotedColumn .= '(' . $length . ')';
109
            }
110
111 6466
            $columns[] = $quotedColumn;
112
        }
113
114 6488
        return $columns;
115
    }
116
117
    /**
118
     * @return string[]
119
     */
120 23
    public function getUnquotedColumns()
121
    {
122 23
        return array_map([$this, 'trimQuotes'], $this->getColumns());
123
    }
124
125
    /**
126
     * Is the index neither unique nor primary key?
127
     *
128
     * @return bool
129
     */
130 22
    public function isSimpleIndex()
131
    {
132 22
        return ! $this->_isPrimary && ! $this->_isUnique;
133
    }
134
135
    /**
136
     * @return bool
137
     */
138 4433
    public function isUnique()
139
    {
140 4433
        return $this->_isUnique;
141
    }
142
143
    /**
144
     * @return bool
145
     */
146 9839
    public function isPrimary()
147
    {
148 9839
        return $this->_isPrimary;
149
    }
150
151
    /**
152
     * @param string $columnName
153
     * @param int    $pos
154
     *
155
     * @return bool
156
     */
157 23
    public function hasColumnAtPosition($columnName, $pos = 0)
158
    {
159 23
        $columnName   = $this->trimQuotes(strtolower($columnName));
160 23
        $indexColumns = array_map('strtolower', $this->getUnquotedColumns());
161
162 23
        return array_search($columnName, $indexColumns, true) === $pos;
163
    }
164
165
    /**
166
     * Checks if this index exactly spans the given column names in the correct order.
167
     *
168
     * @param string[] $columnNames
169
     *
170
     * @return bool
171
     */
172 2319
    public function spansColumns(array $columnNames)
173
    {
174 2319
        $columns         = $this->getColumns();
175 2319
        $numberOfColumns = count($columns);
176 2319
        $sameColumns     = true;
177
178 2319
        for ($i = 0; $i < $numberOfColumns; $i++) {
179 2319
            if (isset($columnNames[$i]) && $this->trimQuotes(strtolower($columns[$i])) === $this->trimQuotes(strtolower($columnNames[$i]))) {
180 1609
                continue;
181
            }
182
183 1094
            $sameColumns = false;
184
        }
185
186 2319
        return $sameColumns;
187
    }
188
189
    /**
190
     * Checks if the other index already fulfills all the indexing and constraint needs of the current one.
191
     *
192
     * @return bool
193
     */
194 2451
    public function isFullfilledBy(Index $other)
195
    {
196
        // allow the other index to be equally large only. It being larger is an option
197
        // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo)
198 2451
        if (count($other->getColumns()) !== count($this->getColumns())) {
199 308
            return false;
200
        }
201
202
        // Check if columns are the same, and even in the same order
203 2231
        $sameColumns = $this->spansColumns($other->getColumns());
204
205 2231
        if ($sameColumns) {
0 ignored issues
show
introduced by
The condition $sameColumns is always true.
Loading history...
206 1521
            if (! $this->samePartialIndex($other)) {
207 22
                return false;
208
            }
209
210 1521
            if (! $this->hasSameColumnLengths($other)) {
211 44
                return false;
212
            }
213
214 1477
            if (! $this->isUnique() && ! $this->isPrimary()) {
215
                // this is a special case: If the current key is neither primary or unique, any unique or
216
                // primary key will always have the same effect for the index and there cannot be any constraint
217
                // overlaps. This means a primary or unique index can always fulfill the requirements of just an
218
                // index that has no constraints.
219 1151
                return true;
220
            }
221
222 369
            if ($other->isPrimary() !== $this->isPrimary()) {
223 100
                return false;
224
            }
225
226 291
            return $other->isUnique() === $this->isUnique();
227
        }
228
229 1094
        return false;
230
    }
231
232
    /**
233
     * Detects if the other index is a non-unique, non primary index that can be overwritten by this one.
234
     *
235
     * @return bool
236
     */
237 22
    public function overrules(Index $other)
238
    {
239 22
        if ($other->isPrimary()) {
240
            return false;
241
        }
242
243 22
        if ($this->isSimpleIndex() && $other->isUnique()) {
244
            return false;
245
        }
246
247 22
        return $this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other);
248
    }
249
250
    /**
251
     * Returns platform specific flags for indexes.
252
     *
253
     * @return string[]
254
     */
255 285
    public function getFlags()
256
    {
257 285
        return array_keys($this->_flags);
258
    }
259
260
    /**
261
     * Adds Flag for an index that translates to platform specific handling.
262
     *
263
     * @param string $flag
264
     *
265
     * @return Index
266
     *
267
     * @example $index->addFlag('CLUSTERED')
268
     */
269 412
    public function addFlag($flag)
270
    {
271 412
        $this->_flags[strtolower($flag)] = true;
272
273 412
        return $this;
274
    }
275
276
    /**
277
     * Does this index have a specific flag?
278
     *
279
     * @param string $flag
280
     *
281
     * @return bool
282
     */
283 1950
    public function hasFlag($flag)
284
    {
285 1950
        return isset($this->_flags[strtolower($flag)]);
286
    }
287
288
    /**
289
     * Removes a flag.
290
     *
291
     * @param string $flag
292
     *
293
     * @return void
294
     */
295 22
    public function removeFlag($flag)
296
    {
297 22
        unset($this->_flags[strtolower($flag)]);
298 22
    }
299
300
    /**
301
     * @param string $name
302
     *
303
     * @return bool
304
     */
305 4846
    public function hasOption($name)
306
    {
307 4846
        return isset($this->options[strtolower($name)]);
308
    }
309
310
    /**
311
     * @param string $name
312
     *
313
     * @return mixed
314
     */
315 139
    public function getOption($name)
316
    {
317 139
        return $this->options[strtolower($name)];
318
    }
319
320
    /**
321
     * @return mixed[]
322
     */
323 285
    public function getOptions()
324
    {
325 285
        return $this->options;
326
    }
327
328
    /**
329
     * Return whether the two indexes have the same partial index
330
     *
331
     * @return bool
332
     */
333 1543
    private function samePartialIndex(Index $other)
334
    {
335 1543
        if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') === $other->getOption('where')) {
336 49
            return true;
337
        }
338
339 1538
        return ! $this->hasOption('where') && ! $other->hasOption('where');
340
    }
341
342
    /**
343
     * Returns whether the index has the same column lengths as the other
344
     */
345 1521
    private function hasSameColumnLengths(self $other) : bool
346
    {
347
        $filter = static function (?int $length) : bool {
348 277
            return $length !== null;
349 1521
        };
350
351 1521
        return array_filter($this->options['lengths'] ?? [], $filter)
352 1521
            === array_filter($other->options['lengths'] ?? [], $filter);
353
    }
354
}
355