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

Index::getColumns()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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