Completed
Push — 2.9 ( dac7d6...c7450a )
by Sergei
58:35 queued 41:25
created

Index   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 340
Duplicated Lines 0 %

Test Coverage

Coverage 96.88%

Importance

Changes 0
Metric Value
wmc 50
eloc 77
dl 0
loc 340
rs 8.4
c 0
b 0
f 0
ccs 93
cts 96
cp 0.9688

21 Methods

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

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