Failed Conditions
Push — master ( 0dbcf1...e125a4 )
by Marco
19:24
created

Index   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 320
Duplicated Lines 0 %

Test Coverage

Coverage 96.34%

Importance

Changes 0
Metric Value
wmc 45
dl 0
loc 320
ccs 79
cts 82
cp 0.9634
rs 8.3673
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A hasColumnAtPosition() 0 6 1
A spansColumns() 0 13 4
A getUnquotedColumns() 0 3 1
A isUnique() 0 3 1
A isPrimary() 0 3 1
A __construct() 0 14 4
A getColumns() 0 3 1
A isSimpleIndex() 0 3 2
A _addColumn() 0 6 2
A getQuotedColumns() 0 9 2
A hasFlag() 0 3 1
A getOptions() 0 3 1
A getOption() 0 3 1
A addFlag() 0 5 1
B samePartialIndex() 0 7 5
C isFullfilledBy() 0 32 7
A getFlags() 0 3 1
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
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Schema;
21
22
use Doctrine\DBAL\Platforms\AbstractPlatform;
23
24
class Index extends AbstractAsset implements Constraint
25
{
26
    /**
27
     * Asset identifier instances of the column names the index is associated with.
28
     * array($columnName => Identifier)
29
     *
30
     * @var Identifier[]
31
     */
32
    protected $_columns = [];
33
34
    /**
35
     * @var bool
36
     */
37
    protected $_isUnique = false;
38
39
    /**
40
     * @var bool
41
     */
42
    protected $_isPrimary = false;
43
44
    /**
45
     * Platform specific flags for indexes.
46
     * array($flagName => true)
47
     *
48
     * @var array
49
     */
50
    protected $_flags = [];
51
52
    /**
53
     * Platform specific options
54
     *
55
     * @todo $_flags should eventually be refactored into options
56
     *
57
     * @var array
58
     */
59
    private $options = [];
60
61
    /**
62
     * @param string   $indexName
63
     * @param string[] $columns
64
     * @param bool     $isUnique
65
     * @param bool     $isPrimary
66
     * @param string[] $flags
67
     * @param array    $options
68
     */
69 673
    public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = [], array $options = [])
70
    {
71 673
        $isUnique = $isUnique || $isPrimary;
72
73 673
        $this->_setName($indexName);
74 673
        $this->_isUnique = $isUnique;
75 673
        $this->_isPrimary = $isPrimary;
76 673
        $this->options = $options;
77
78 673
        foreach ($columns as $column) {
79 652
            $this->_addColumn($column);
80
        }
81 673
        foreach ($flags as $flag) {
82 18
            $this->addFlag($flag);
83
        }
84 673
    }
85
86
    /**
87
     * @param string $column
88
     *
89
     * @return void
90
     *
91
     * @throws \InvalidArgumentException
92
     */
93 652
    protected function _addColumn($column)
94
    {
95 652
        if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) can never be false.
Loading history...
96 652
            $this->_columns[$column] = new Identifier($column);
97
        } else {
98
            throw new \InvalidArgumentException("Expecting a string as Index Column");
99
        }
100 652
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105 329
    public function getColumns()
106
    {
107 329
        return array_keys($this->_columns);
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113 420
    public function getQuotedColumns(AbstractPlatform $platform)
114
    {
115 420
        $columns = [];
116
117 420
        foreach ($this->_columns as $column) {
118 412
            $columns[] = $column->getQuotedName($platform);
119
        }
120
121 420
        return $columns;
122
    }
123
124
    /**
125
     * @return string[]
126
     */
127 1
    public function getUnquotedColumns()
128
    {
129 1
        return array_map([$this, 'trimQuotes'], $this->getColumns());
130
    }
131
132
    /**
133
     * Is the index neither unique nor primary key?
134
     *
135
     * @return bool
136
     */
137 1
    public function isSimpleIndex()
138
    {
139 1
        return !$this->_isPrimary && !$this->_isUnique;
140
    }
141
142
    /**
143
     * @return bool
144
     */
145 298
    public function isUnique()
146
    {
147 298
        return $this->_isUnique;
148
    }
149
150
    /**
151
     * @return bool
152
     */
153 624
    public function isPrimary()
154
    {
155 624
        return $this->_isPrimary;
156
    }
157
158
    /**
159
     * @param string $columnName
160
     * @param int    $pos
161
     *
162
     * @return bool
163
     */
164 1
    public function hasColumnAtPosition($columnName, $pos = 0)
165
    {
166 1
        $columnName   = $this->trimQuotes(strtolower($columnName));
167 1
        $indexColumns = array_map('strtolower', $this->getUnquotedColumns());
168
169 1
        return array_search($columnName, $indexColumns) === $pos;
170
    }
171
172
    /**
173
     * Checks if this index exactly spans the given column names in the correct order.
174
     *
175
     * @param array $columnNames
176
     *
177
     * @return bool
178
     */
179 124
    public function spansColumns(array $columnNames)
180
    {
181 124
        $columns         = $this->getColumns();
182 124
        $numberOfColumns = count($columns);
183 124
        $sameColumns     = true;
184
185 124
        for ($i = 0; $i < $numberOfColumns; $i++) {
186 124
            if ( ! isset($columnNames[$i]) || $this->trimQuotes(strtolower($columns[$i])) !== $this->trimQuotes(strtolower($columnNames[$i]))) {
187 60
                $sameColumns = false;
188
            }
189
        }
190
191 124
        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
     * @param Index $other
198
     *
199
     * @return bool
200
     */
201 130
    public function isFullfilledBy(Index $other)
202
    {
203
        // allow the other index to be equally large only. It being larger is an option
204
        // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo)
205 130
        if (count($other->getColumns()) != count($this->getColumns())) {
206 17
            return false;
207
        }
208
209
        // Check if columns are the same, and even in the same order
210 120
        $sameColumns = $this->spansColumns($other->getColumns());
211
212 120
        if ($sameColumns) {
213 83
            if ( ! $this->samePartialIndex($other)) {
214 1
                return false;
215
            }
216
217 83
            if ( ! $this->isUnique() && ! $this->isPrimary()) {
218
                // this is a special case: If the current key is neither primary or unique, any unique or
219
                // primary key will always have the same effect for the index and there cannot be any constraint
220
                // overlaps. This means a primary or unique index can always fulfill the requirements of just an
221
                // index that has no constraints.
222 61
                return true;
223
            }
224
225 22
            if ($other->isPrimary() != $this->isPrimary()) {
226 4
                return false;
227
            }
228
229 19
            return $other->isUnique() === $this->isUnique();
230
        }
231
232 60
        return false;
233
    }
234
235
    /**
236
     * Detects if the other index is a non-unique, non primary index that can be overwritten by this one.
237
     *
238
     * @param Index $other
239
     *
240
     * @return bool
241
     */
242 1
    public function overrules(Index $other)
243
    {
244 1
        if ($other->isPrimary()) {
245
            return false;
246 1
        } elseif ($this->isSimpleIndex() && $other->isUnique()) {
247
            return false;
248
        }
249
250 1
        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 12
    public function getFlags()
259
    {
260 12
        return array_keys($this->_flags);
261
    }
262
263
    /**
264
     * Adds Flag for an index that translates to platform specific handling.
265
     *
266
     * @example $index->addFlag('CLUSTERED')
267
     *
268
     * @param string $flag
269
     *
270
     * @return Index
271
     */
272 35
    public function addFlag($flag)
273
    {
274 35
        $this->_flags[strtolower($flag)] = true;
275
276 35
        return $this;
277
    }
278
279
    /**
280
     * Does this index have a specific flag?
281
     *
282
     * @param string $flag
283
     *
284
     * @return bool
285
     */
286 160
    public function hasFlag($flag)
287
    {
288 160
        return isset($this->_flags[strtolower($flag)]);
289
    }
290
291
    /**
292
     * Removes a flag.
293
     *
294
     * @param string $flag
295
     *
296
     * @return void
297
     */
298 1
    public function removeFlag($flag)
299
    {
300 1
        unset($this->_flags[strtolower($flag)]);
301 1
    }
302
303
    /**
304
     * @param string $name
305
     *
306
     * @return bool
307
     */
308 125
    public function hasOption($name)
309
    {
310 125
        return isset($this->options[strtolower($name)]);
311
    }
312
313
    /**
314
     * @param string $name
315
     *
316
     * @return mixed
317
     */
318 8
    public function getOption($name)
319
    {
320 8
        return $this->options[strtolower($name)];
321
    }
322
323
    /**
324
     * @return array
325
     */
326 12
    public function getOptions()
327
    {
328 12
        return $this->options;
329
    }
330
331
    /**
332
     * Return whether the two indexes have the same partial index
333
     * @param \Doctrine\DBAL\Schema\Index $other
334
     *
335
     * @return bool
336
     */
337 84
    private function samePartialIndex(Index $other)
338
    {
339 84
        if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') == $other->getOption('where')) {
340 2
            return true;
341
        }
342
343 84
        return ! $this->hasOption('where') && ! $other->hasOption('where');
344
    }
345
346
}
347