Completed
Pull Request — master (#2412)
by Benoît
20:15
created

Index   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 323
Duplicated Lines 0 %

Test Coverage

Coverage 96.63%

Importance

Changes 0
Metric Value
wmc 48
eloc 71
dl 0
loc 323
ccs 86
cts 89
cp 0.9663
rs 8.5599
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A hasFlag() 0 3 1
A getOptions() 0 3 1
A getQuotedColumns() 0 20 5
A _addColumn() 0 7 2
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 14 4
A samePartialIndex() 0 7 5
B isFullfilledBy() 0 32 7
A getColumns() 0 3 1
A getFlags() 0 3 1
A isSimpleIndex() 0 3 2
A hasOption() 0 3 1
B overrules() 0 9 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_keys;
8
use function array_map;
9
use function array_search;
10
use function array_shift;
11
use function count;
12
use function is_string;
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 13131
    public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = [], array $options = [])
56
    {
57 13131
        $isUnique = $isUnique || $isPrimary;
58
59 13131
        $this->_setName($indexName);
60 13131
        $this->_isUnique  = $isUnique;
61 13131
        $this->_isPrimary = $isPrimary;
62 13131
        $this->options    = $options;
63
64 13131
        foreach ($columns as $column) {
65 12732
            $this->_addColumn($column);
66
        }
67 13131
        foreach ($flags as $flag) {
68 408
            $this->addFlag($flag);
69
        }
70 13131
    }
71
72
    /**
73
     * @param string $column
74
     *
75
     * @return void
76
     *
77
     * @throws InvalidArgumentException
78
     */
79 12732
    protected function _addColumn($column)
80
    {
81 12732
        if (! is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always true.
Loading history...
82
            throw new InvalidArgumentException('Expecting a string as Index Column');
83
        }
84
85 12732
        $this->_columns[$column] = new Identifier($column);
86 12732
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91 9742
    public function getColumns()
92
    {
93 9742
        return array_keys($this->_columns);
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99 8328
    public function getQuotedColumns(AbstractPlatform $platform)
100
    {
101 8328
        $subParts = $platform->supportsColumnLengthIndexes() && $this->hasOption('lengths')
102 8328
            ? $this->getOption('lengths') : [];
103
104 8328
        $columns = [];
105
106 8328
        foreach ($this->_columns as $column) {
107 8176
            $length = array_shift($subParts);
108
109 8176
            $quotedColumn = $column->getQuotedName($platform);
110
111 8176
            if ($length !== null) {
112 8
                $quotedColumn .= '(' . $length . ')';
113
            }
114
115 8176
            $columns[] = $quotedColumn;
116
        }
117
118 8328
        return $columns;
119
    }
120
121
    /**
122
     * @return string[]
123
     */
124 20
    public function getUnquotedColumns()
125
    {
126 20
        return array_map([$this, 'trimQuotes'], $this->getColumns());
127
    }
128
129
    /**
130
     * Is the index neither unique nor primary key?
131
     *
132
     * @return bool
133
     */
134 19
    public function isSimpleIndex()
135
    {
136 19
        return ! $this->_isPrimary && ! $this->_isUnique;
137
    }
138
139
    /**
140
     * @return bool
141
     */
142 5800
    public function isUnique()
143
    {
144 5800
        return $this->_isUnique;
145
    }
146
147
    /**
148
     * @return bool
149
     */
150 12218
    public function isPrimary()
151
    {
152 12218
        return $this->_isPrimary;
153
    }
154
155
    /**
156
     * @param string $columnName
157
     * @param int    $pos
158
     *
159
     * @return bool
160
     */
161 20
    public function hasColumnAtPosition($columnName, $pos = 0)
162
    {
163 20
        $columnName   = $this->trimQuotes(strtolower($columnName));
164 20
        $indexColumns = array_map('strtolower', $this->getUnquotedColumns());
165
166 20
        return array_search($columnName, $indexColumns) === $pos;
167
    }
168
169
    /**
170
     * Checks if this index exactly spans the given column names in the correct order.
171
     *
172
     * @param string[] $columnNames
173
     *
174
     * @return bool
175
     */
176 2472
    public function spansColumns(array $columnNames)
177
    {
178 2472
        $columns         = $this->getColumns();
179 2472
        $numberOfColumns = count($columns);
180 2472
        $sameColumns     = true;
181
182 2472
        for ($i = 0; $i < $numberOfColumns; $i++) {
183 2472
            if (isset($columnNames[$i]) && $this->trimQuotes(strtolower($columns[$i])) === $this->trimQuotes(strtolower($columnNames[$i]))) {
184 1703
                continue;
185
            }
186
187 1310
            $sameColumns = false;
188
        }
189
190 2472
        return $sameColumns;
191
    }
192
193
    /**
194
     * Checks if the other index already fulfills all the indexing and constraint needs of the current one.
195
     *
196
     * @return bool
197
     */
198 2586
    public function isFullfilledBy(Index $other)
199
    {
200
        // allow the other index to be equally large only. It being larger is an option
201
        // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo)
202 2586
        if (count($other->getColumns()) !== count($this->getColumns())) {
203 323
            return false;
204
        }
205
206
        // Check if columns are the same, and even in the same order
207 2396
        $sameColumns = $this->spansColumns($other->getColumns());
208
209 2396
        if ($sameColumns) {
0 ignored issues
show
introduced by
The condition $sameColumns is always true.
Loading history...
210 1627
            if (! $this->samePartialIndex($other)) {
211 19
                return false;
212
            }
213
214 1627
            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 1297
                return true;
220
            }
221
222 362
            if ($other->isPrimary() !== $this->isPrimary()) {
223 84
                return false;
224
            }
225
226 297
            return $other->isUnique() === $this->isUnique();
227
        }
228
229 1310
        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 19
    public function overrules(Index $other)
238
    {
239 19
        if ($other->isPrimary()) {
240
            return false;
241 19
        } elseif ($this->isSimpleIndex() && $other->isUnique()) {
242
            return false;
243
        }
244
245 19
        return $this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other);
246
    }
247
248
    /**
249
     * Returns platform specific flags for indexes.
250
     *
251
     * @return string[]
252
     */
253 246
    public function getFlags()
254
    {
255 246
        return array_keys($this->_flags);
256
    }
257
258
    /**
259
     * Adds Flag for an index that translates to platform specific handling.
260
     *
261
     * @param string $flag
262
     *
263
     * @return Index
264
     *
265
     * @example $index->addFlag('CLUSTERED')
266
     */
267 731
    public function addFlag($flag)
268
    {
269 731
        $this->_flags[strtolower($flag)] = true;
270
271 731
        return $this;
272
    }
273
274
    /**
275
     * Does this index have a specific flag?
276
     *
277
     * @param string $flag
278
     *
279
     * @return bool
280
     */
281 3399
    public function hasFlag($flag)
282
    {
283 3399
        return isset($this->_flags[strtolower($flag)]);
284
    }
285
286
    /**
287
     * Removes a flag.
288
     *
289
     * @param string $flag
290
     *
291
     * @return void
292
     */
293 19
    public function removeFlag($flag)
294
    {
295 19
        unset($this->_flags[strtolower($flag)]);
296 19
    }
297
298
    /**
299
     * @param string $name
300
     *
301
     * @return bool
302
     */
303 4787
    public function hasOption($name)
304
    {
305 4787
        return isset($this->options[strtolower($name)]);
306
    }
307
308
    /**
309
     * @param string $name
310
     *
311
     * @return mixed
312
     */
313 166
    public function getOption($name)
314
    {
315 166
        return $this->options[strtolower($name)];
316
    }
317
318
    /**
319
     * @return mixed[]
320
     */
321 246
    public function getOptions()
322
    {
323 246
        return $this->options;
324
    }
325
326
    /**
327
     * Return whether the two indexes have the same partial index
328
     *
329
     * @return bool
330
     */
331 1646
    private function samePartialIndex(Index $other)
332
    {
333 1646
        if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') === $other->getOption('where')) {
334 44
            return true;
335
        }
336
337 1640
        return ! $this->hasOption('where') && ! $other->hasOption('where');
338
    }
339
}
340