Completed
Push — master ( 4a233e...d91d64 )
by Edgard
11:56
created

Schema::findColumns()   D

Complexity

Conditions 14
Paths 50

Size

Total Lines 84
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 14.3828

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 84
ccs 28
cts 32
cp 0.875
rs 4.9516
cc 14
eloc 38
nc 50
nop 1
crap 14.3828

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @link http://www.yiiframework.com/
5
 * @copyright Copyright (c) 2008 Yii Software LLC
6
 * @license http://www.yiiframework.com/license/
7
 */
8
9
namespace edgardmessias\db\firebird;
10
11
use yii\base\InvalidCallException;
12
use yii\db\Exception;
13
use yii\db\TableSchema;
14
15
/**
16
 * Schema represents the Firebird schema information.
17
 *
18
 * @property string[] $indexNames All index names in the Firebird. This property is read-only.
19
 * @property IndexSchema[] $indexSchemas The metadata for all indexes in the Firebird. Each array element is an
20
 * instance of [[IndexSchema]] or its child class. This property is read-only.
21
 * @property array $indexTypes All index types in the Firebird in format: index name => index type. This
22
 * property is read-only.
23
 * @property QueryBuilder $queryBuilder The query builder for this connection. This property is read-only.
24
 *
25
 * @author Edgard Lorraine Messias <[email protected]>
26
 * @since 2.0
27
 */
28
class Schema extends \yii\db\Schema
29
{
30
31
    private $_lastInsertID = null;
32
33
    /**
34
     * @var array map of DB errors and corresponding exceptions
35
     * If left part is found in DB error message exception class from the right part is used.
36
     */
37
    public $exceptionMap = [
38
        'SQLSTATE[23'                                               => 'yii\db\IntegrityException',
39
        'SQLSTATE[HY000]: General error: -803 violation of PRIMARY' => 'yii\db\IntegrityException',
40
    ];
41
    public $reservedWords = [
42
        'ORDER',
43
        'POSITION',
44
        'TIME',
45
        'VALUE',
46
    ];
47
48
    /**
49
     * @var array mapping from physical column types (keys) to abstract column types (values)
50
     */
51
    public $typeMap = [
52
        'bigint'             => self::TYPE_BIGINT,
53
        'char'               => self::TYPE_CHAR,
54
        'varchar'            => self::TYPE_STRING,
55
        'timestamp'          => self::TYPE_TIMESTAMP,
56
        'decimal'            => self::TYPE_DECIMAL,
57
        'float'              => self::TYPE_FLOAT,
58
        'blob'               => self::TYPE_BINARY,
59
        'integer'            => self::TYPE_INTEGER,
60
        'blob sub_type text' => self::TYPE_TEXT,
61
        'numeric'            => self::TYPE_DECIMAL,
62
        'double precision'   => self::TYPE_DOUBLE,
63
        'smallint'           => self::TYPE_SMALLINT,
64
    ];
65
66
    /**
67
     * Creates a query builder for the database.
68
     * This method may be overridden by child classes to create a DBMS-specific query builder.
69
     * @return QueryBuilder query builder instance
70
     */
71 99
    public function createQueryBuilder()
72
    {
73 99
        return new QueryBuilder($this->db);
74
    }
75
76
    /**
77
     * @inheritdoc
78
     */
79 2
    public function createColumnSchemaBuilder($type, $length = null)
80
    {
81 2
        return new ColumnSchemaBuilder($type, $length);
82
    }
83
84 157
    public function quoteSimpleTableName($name)
85
    {
86 157
        if ($this->db->tablePrefix !== '') {
87
            return $name;
88
        }
89
90 157
        $word = strtoupper(str_replace('%', '', $name));
91 157
        if (in_array($word, $this->reservedWords)) {
92 43
            return strpos($name, '"') !== false ? $name : '"' . $name . '"';
93
        }
94
95 156
        return $name;
96
    }
97
98 173
    public function quoteSimpleColumnName($name)
99
    {
100 173
        if (in_array(strtoupper($name), $this->reservedWords)) {
101 6
            return parent::quoteSimpleColumnName($name);
102
        }
103 173
        return $name;
104
    }
105
106 106
    protected function loadTableSchema($name)
107
    {
108 106
        $table = new TableSchema;
109 106
        $this->resolveTableNames($table, $name);
110 106
        if ($this->findColumns($table)) {
111 104
            $this->findConstraints($table);
112 104
            return $table;
113
        }
114 7
        return null;
115
    }
116
117 93
    public function getPdoType($data)
118
    {
119 93
        static $typeMap = [
120
            // php type => PDO type
121
            'boolean'  => \PDO::PARAM_INT,
122
            'integer'  => \PDO::PARAM_INT,
123
            'string'   => \PDO::PARAM_STR,
124
            'resource' => \PDO::PARAM_LOB,
125
            'NULL'     => \PDO::PARAM_NULL,
126
        ];
127 93
        $type = gettype($data);
128
129 93
        return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
130
    }
131
132
    /**
133
     *
134
     * @param TableSchema $table
135
     * @param string $name
136
     */
137 106
    protected function resolveTableNames($table, $name)
138
    {
139 106
        $parts = explode('.', str_replace('"', '', $name));
140 106
        if (isset($parts[1])) {
141
            $table->schemaName = $parts[0];
142
            $table->name = strtolower($parts[1]);
143
            $table->fullName = $this->quoteTableName($table->schemaName) . '.' . $this->quoteTableName($table->name);
144
        } else {
145 106
            $table->name = strtolower($parts[0]);
146 106
            $table->fullName = $this->quoteTableName($table->name);
147
        }
148 106
    }
149
150
    /**
151
     * Collects the table column metadata.
152
     *
153
     * @param TableSchema $table the table metadata
154
     * @return boolean whether the table exists in the database
155
     */
156 106
    protected function findColumns($table)
157
    {
158
        // Zoggo - Converted sql to use join syntax
159
        // robregonm - Added isAutoInc
160
        $sql = 'SELECT
161
                    rel.rdb$field_name AS fname,
162
                    rel.rdb$default_source AS fdefault,
163
                    fld.rdb$field_type AS fcodtype,
164
                    fld.rdb$field_sub_type AS fcodsubtype,
165
                    fld.rdb$field_length AS flength,
166
                    fld.rdb$character_length AS fcharlength,
167
                    fld.rdb$field_scale AS fscale,
168
                    fld.rdb$field_precision AS fprecision,
169
                    rel.rdb$null_flag AS fnull,
170
                    rel.rdb$description AS fcomment,
171
                    fld.rdb$default_value AS fdefault_value,';
172
173
        if (version_compare($this->db->firebird_version, '3.0.0', '>=')) {
174 106
            $sql .= '
175
                    rel.rdb$generator_name AS fgenerator_name,';
176
        }
177
        
178
        $sql .= '
179
                    (SELECT RDB$TRIGGER_SOURCE FROM RDB$TRIGGERS
180
                        WHERE RDB$SYSTEM_FLAG = 0
181
                        AND UPPER(RDB$RELATION_NAME)=UPPER(\'' . $table->name . '\')
182
                        AND RDB$TRIGGER_TYPE = 1
183
                        AND RDB$TRIGGER_INACTIVE = 0
184 106
                        AND (UPPER(REPLACE(RDB$TRIGGER_SOURCE,\' \',\'\')) LIKE \'%NEW.\'||TRIM(rel.rdb$field_name)||\'=GEN_ID%\'
185
                            OR UPPER(REPLACE(RDB$TRIGGER_SOURCE,\' \',\'\')) LIKE \'%NEW.\'||TRIM(rel.rdb$field_name)||\'=NEXTVALUEFOR%\'))
186 106
                    AS fautoinc
187
                FROM
188 106
                    rdb$relation_fields rel
189 106
                    JOIN rdb$fields fld ON rel.rdb$field_source=fld.rdb$field_name
190 106
                WHERE
191
                    UPPER(rel.rdb$relation_name)=UPPER(\'' . $table->name . '\')
192
                ORDER BY
193
                    rel.rdb$field_position;';
194
        try {
195
            $columns = $this->db->createCommand($sql)->queryAll();
196
            if (empty($columns)) {
197
                return false;
198
            }
199
        } catch (Exception $e) {
200
            return false;
201 104
        }
202
        $sql = 'SELECT
203 104
                    idx.rdb$field_name AS fname
204
                FROM
205
                    rdb$relation_constraints rc
206
                    JOIN rdb$index_segments idx ON idx.rdb$index_name=rc.rdb$index_name
207 104
                WHERE rc.rdb$constraint_type=\'PRIMARY KEY\'
208 104
					AND UPPER(rc.rdb$relation_name)=UPPER(\'' . $table->name . '\')';
209 104
        try {
210 104
            $pkeys = $this->db->createCommand($sql)->queryColumn();
211 104
        } catch (Exception $e) {
212
            return false;
213 104
        }
214 104
        $pkeys = array_map('rtrim', $pkeys);
215 104
        $pkeys = array_map('strtolower', $pkeys);
216 87
        foreach ($columns as $key => $column) {
217 87
            $column = array_map('strtolower', $column);
218 79
            $columns[$key]['fprimary'] = in_array(rtrim($column['fname']), $pkeys);
219 13
        }
220 13
        foreach ($columns as $column) {
221
            $c = $this->loadColumnSchema($column);
222
            if ($table->sequenceName === null && $c->autoIncrement) {
223 104
                $matches = [];
224 104
                
225 104
                if (isset($column['fgenerator_name']) && $column['fgenerator_name']) {
226
                    $table->sequenceName = $column['fgenerator_name'];
227
                } elseif (preg_match("/NEW.{$c->name}\s*=\s*GEN_ID\((\w+)/i", $column['fautoinc'], $matches)) {
228 104
                    $table->sequenceName = $matches[1];
229
                } elseif (preg_match("/NEW.{$c->name}\s*=\s*NEXT\s+VALUE\s+FOR\s+(\w+)/i", $column['fautoinc'], $matches)) {
230
                    $table->sequenceName = $matches[1];
231
                }
232
            }
233
            $table->columns[$c->name] = $c;
234
            if ($c->isPrimaryKey) {
235 104
                $table->primaryKey[] = $c->name;
236
            }
237 104
        }
238
        return (count($table->columns) > 0);
239
    }
240
241
    /**
242
     * @return ColumnSchema
243
     * @throws \yii\base\InvalidConfigException
244
     */
245
    protected function createColumnSchema()
246 104
    {
247
        return \Yii::createObject('\edgardmessias\db\firebird\ColumnSchema');
248 104
    }
249 104
250 104
    /**
251 104
     * Creates a table column.
252 104
     *
253 104
     * @param array $column column metadata
254
     * @return ColumnSchema normalized column metadata
255 104
     */
256
    protected function loadColumnSchema($column)
257 104
    {
258 104
        $c = $this->createColumnSchema();
259
        $c->name = strtolower(rtrim($column['fname']));
260 63
        $c->allowNull = (int) $column['fnull'] !== 1;
261 63
        $c->isPrimaryKey = $column['fprimary'];
262
        $c->autoIncrement = (isset($column['fgenerator_name']) && $column['fgenerator_name']) || (boolean) $column['fautoinc'];
263
        $c->comment = $column['fcomment'] === null ? '' : $column['fcomment'];
264 63
265
        $c->type = self::TYPE_STRING;
266 104
267 103
        $defaultValue = null;
268
        if (!empty($column['fdefault'])) {
269 104
            // remove whitespace, 'DEFAULT ' prefix and surrounding single quotes; all optional
270
            if (preg_match("/\s*(DEFAULT\s+){0,1}('(.*)'|(.*))\s*/i", $column['fdefault'], $parts)) {
271 104
                $defaultValue = array_pop($parts);
272
            }
273
            // handle escaped single quotes like in "funny''quoted''string"
274
            $defaultValue = str_replace('\'\'', '\'', $defaultValue);
275
        }
276
        if ($defaultValue === null) {
277
            $defaultValue = $column['fdefault_value'];
278
        }
279
        $dbType = '';
280
        $baseTypes = [
281
            7   => 'SMALLINT',
282
            8   => 'INTEGER',
283
            16  => 'INT64',
284
            9   => 'QUAD',
285
            10  => 'FLOAT',
286
            11  => 'D_FLOAT',
287 104
            17  => 'BOOLEAN',
288
            27  => 'DOUBLE PRECISION',
289
            12  => 'DATE',
290 104
            13  => 'TIME',
291 104
            35  => 'TIMESTAMP',
292 88
            261 => 'BLOB',
293 88
            40  => 'CSTRING',
294 88
            45  => 'BLOB_ID',
295 88
        ];
296
        $baseCharTypes = [
297 104
            37 => 'VARCHAR',
298 104
            14 => 'CHAR',
299 104
        ];
300 104
        if (array_key_exists((int) $column['fcodtype'], $baseTypes)) {
301 104
            $dbType = $baseTypes[(int) $column['fcodtype']];
302
        } elseif (array_key_exists((int) $column['fcodtype'], $baseCharTypes)) {
303
            $c->size = (int) $column['fcharlength'];
304
            $c->precision = $c->size;
305
            $dbType = $baseCharTypes[(int) $column['fcodtype']] . "($c->size)";
306
        }
307 104
        switch ((int) $column['fcodtype']) {
308 16
            case 7:
309 16
            case 8:
310 16
                switch ((int) $column['fcodsubtype']) {
311 16
                    case 1:
312 16
                        $c->precision = (int) $column['fprecision'];
313
                        $c->size = $c->precision;
314 104
                        $c->scale = abs((int) $column['fscale']);
0 ignored issues
show
Documentation Bug introduced by
It seems like abs((int) $column['fscale']) can also be of type double. However, the property $scale is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
315 101
                        $dbType = "NUMERIC({$c->precision},{$c->scale})";
316 47
                        break;
317 47
                    case 2:
318 3
                        $c->precision = (int) $column['fprecision'];
319 3
                        $c->size = $c->precision;
320 3
                        $c->scale = abs((int) $column['fscale']);
321 3
                        $dbType = "DECIMAL({$c->precision},{$c->scale})";
322 3
                        break;
323 47
                }
324 46
                break;
325 46
            case 16:
326 46
                switch ((int) $column['fcodsubtype']) {
327 46
                    case 1:
328 46
                        $c->precision = (int) $column['fprecision'];
329
                        $c->size = $c->precision;
330 4
                        $c->scale = abs((int) $column['fscale']);
331 4
                        $dbType = "NUMERIC({$c->precision},{$c->scale})";
332
                        break;
333 47
                    case 2:
334 89
                        $c->precision = (int) $column['fprecision'];
335 15
                        $c->size = $c->precision;
336 15
                        $c->scale = abs((int) $column['fscale']);
337 15
                        $dbType = "DECIMAL({$c->precision},{$c->scale})";
338 15
                        break;
339 15
                    default:
340
                        $dbType = 'BIGINT';
341 15
                        break;
342
                }
343
                break;
344 104
            case 261:
345
                switch ((int) $column['fcodsubtype']) {
346 104
                    case 1:
347 104
                        $dbType = 'BLOB SUB_TYPE TEXT';
348 104
                        $c->size = null;
349 104
                        break;
350 104
                }
351
                break;
352
        }
353
354
        $c->dbType = strtolower($dbType);
355 104
356
        $c->type = self::TYPE_STRING;
357 104
        if (preg_match('/^([\w\ ]+)(?:\(([^\)]+)\))?/', $c->dbType, $matches)) {
358 104
            $type = strtolower($matches[1]);
359 63
            if (isset($this->typeMap[$type])) {
360 63
                $c->type = $this->typeMap[$type];
361 15
            }
362
        }
363 63
364
365
        $c->phpType = $this->getColumnPhpType($c);
366
367 104
        $c->defaultValue = null;
368
        if ($defaultValue !== null) {
369
            if (in_array($c->type, [self::TYPE_DATE, self::TYPE_DATETIME, self::TYPE_TIME, self::TYPE_TIMESTAMP])
370
                    && preg_match('/(CURRENT_|NOW|NULL|TODAY|TOMORROW|YESTERDAY)/i', $defaultValue)) {
371
                $c->defaultValue = new \yii\db\Expression(trim($defaultValue));
372
            } else {
373
                $c->defaultValue = $c->phpTypecast($defaultValue);
374
            }
375 104
        }
376
377
        return $c;
378
    }
379
380
    /**
381
     * Collects the foreign key column details for the given table.
382
     *
383
     * @param TableSchema $table the table metadata
384
     */
385
    protected function findConstraints($table)
386
    {
387
        // Zoggo - Converted sql to use join syntax
388
        $sql = 'SELECT
389
                    a.rdb$constraint_name as fconstraint,
390
                    c.rdb$relation_name AS ftable,
391 104
                    d.rdb$field_name AS pfield,
392
                    e.rdb$field_name AS ffield
393 104
                FROM
394
                    rdb$ref_constraints b
395
                    JOIN rdb$relation_constraints a ON a.rdb$constraint_name=b.rdb$constraint_name
396
                    JOIN rdb$relation_constraints c ON b.rdb$const_name_uq=c.rdb$constraint_name
397
                    JOIN rdb$index_segments d ON c.rdb$index_name=d.rdb$index_name
398 104
                    JOIN rdb$index_segments e ON a.rdb$index_name=e.rdb$index_name AND e.rdb$field_position = d.rdb$field_position
399 104
                WHERE
400
                    a.rdb$constraint_type=\'FOREIGN KEY\' AND
401
                    UPPER(a.rdb$relation_name)=UPPER(\'' . $table->name . '\') ';
402 29
        try {
403 29
            $fkeys = $this->db->createCommand($sql)->queryAll();
404
        } catch (Exception $e) {
405 29
            return false;
406 29
        }
407 29
408
        $constraints = [];
409
        foreach ($fkeys as $fkey) {
410 29
            // Zoggo - Added strtolower here to guarantee that values are
411
            // returned lower case. Otherwise gii generates wrong code.
412 104
            $fkey = array_map('rtrim', $fkey);
413 104
            $fkey = array_map('strtolower', $fkey);
414
415 5
            if (!isset($constraints[$fkey['fconstraint']])) {
416
                $constraints[$fkey['fconstraint']] = [
417
                    $fkey['ftable']
418
                ];
419
            }
420
            $constraints[$fkey['fconstraint']][$fkey['ffield']] = $fkey['pfield'];
421
        }
422 5
        $table->foreignKeys = $constraints;
423
    }
424 5
425
    protected function findTableNames($schema = '')
426
    {
427
        $sql = 'SELECT
428
                    rdb$relation_name
429 5
                FROM
430 5
                    rdb$relations
431
                WHERE
432 5
                    (rdb$system_flag is null OR rdb$system_flag=0)';
433
        try {
434
            $tables = $this->db->createCommand($sql)->queryColumn();
435
        } catch (Exception $e) {
436
            return false;
437
        }
438
439
        $tables = array_map('rtrim', $tables);
440
        $tables = array_map('strtolower', $tables);
441
442
        return $tables;
443
    }
444
445
    /**
446
     * Returns all unique indexes for the given table.
447
     * Each array element is of the following structure:
448
     *
449
     * ~~~
450 1
     * [
451
     *  'IndexName1' => ['col1' [, ...]],
452
     *  'IndexName2' => ['col2' [, ...]],
453
     * ]
454
     * ~~~
455
     *
456
     * @param TableSchema $table the table metadata
457
     * @return array all unique indexes for the given table.
458 1
     * @since 2.0.4
459 1
     */
460 1
    public function findUniqueIndexes($table)
461 1
    {
462 1
        $query = '
463 1
SELECT id.RDB$INDEX_NAME as index_name, ids.RDB$FIELD_NAME as column_name
464
FROM RDB$INDICES id
465 1
INNER JOIN RDB$INDEX_SEGMENTS ids ON ids.RDB$INDEX_NAME = id.RDB$INDEX_NAME
466
WHERE id.RDB$UNIQUE_FLAG = 1
467
AND   id.RDB$SYSTEM_FLAG = 0
468
AND UPPER(id.RDB$RELATION_NAME) = UPPER(\'' . $table->name . '\')
469
ORDER BY id.RDB$RELATION_NAME, id.RDB$INDEX_NAME, ids.RDB$FIELD_POSITION';
470
        $result = [];
471
        $command = $this->db->createCommand($query);
472
        foreach ($command->queryAll() as $row) {
473
            $result[strtolower(rtrim($row['index_name']))][] = strtolower(rtrim($row['column_name']));
474
        }
475
        return $result;
476 2
    }
477
478 2
    /**
479 2
     * Sets the isolation level of the current transaction.
480 1
     * @param string $level The transaction isolation level to use for this transaction.
481 1
     * This can be one of [[Transaction::READ_UNCOMMITTED]], [[Transaction::READ_COMMITTED]], [[Transaction::REPEATABLE_READ]]
482 1
     * and [[Transaction::SERIALIZABLE]] but also a string containing DBMS specific syntax to be used
483 1
     * after `SET TRANSACTION ISOLATION LEVEL`.
484
     * @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
485 1
     */
486
    public function setTransactionIsolationLevel($level)
487 2
    {
488
        if ($level == \yii\db\Transaction::READ_UNCOMMITTED) {
489
            parent::setTransactionIsolationLevel('READ COMMITTED RECORD_VERSION');
490
        } elseif ($level == \yii\db\Transaction::REPEATABLE_READ) {
491
            parent::setTransactionIsolationLevel('SNAPSHOT');
492 13
        } elseif ($level == \yii\db\Transaction::SERIALIZABLE) {
493
            parent::setTransactionIsolationLevel('SNAPSHOT TABLE STABILITY');
494 13
        } else {
495 13
            parent::setTransactionIsolationLevel($level);
496 13
        }
497 13
    }
498 13
499 12
    /**
500 12
     * @inheritdoc
501 12
     */
502
    public function insert($table, $columns)
503 12
    {
504
        $this->_lastInsertID = false;
505
        $params = [];
506 13
        $sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
507 13
        $returnColumns = $this->getTableSchema($table)->primaryKey;
508 13
        if (!empty($returnColumns)) {
509
            $returning = [];
510 13
            foreach ((array) $returnColumns as $name) {
511
                $returning[] = $this->quoteColumnName($name);
512
            }
513 13
            $sql .= ' RETURNING ' . implode(', ', $returning);
514 12
        }
515 12
516 10
        $command = $this->db->createCommand($sql, $params);
517 12
        $command->prepare(false);
518
        $result = $command->queryOne();
519
520
        if (!$command->pdoStatement->rowCount()) {
521 13
            return false;
522
        } else {
523
            if (!empty($returnColumns)) {
524
                foreach ((array) $returnColumns as $name) {
525
                    if ($this->getTableSchema($table)->getColumn($name)->autoIncrement) {
526
                        $this->_lastInsertID = $result[$name];
527
                        break;
528 2
                    }
529
                }
530 2
            }
531
            return $result;
532
        }
533
    }
534 2
535 2
    /**
536
     * @inheritdoc
537
     */
538 1
    public function getLastInsertID($sequenceName = '')
539 1
    {
540
        if (!$this->db->isActive) {
541
            throw new InvalidCallException('DB Connection is not active.');
542
        }
543
        
544
        if ($sequenceName !== '') {
545
            return $this->db->createCommand('SELECT GEN_ID(' . $this->db->quoteTableName($sequenceName) . ', 0 ) FROM RDB$DATABASE;')->queryScalar();
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->db->createCommand...BASE;')->queryScalar(); of type string|null|false adds false to the return on line 545 which is incompatible with the return type of the parent method yii\db\Schema::getLastInsertID of type string. It seems like you forgot to handle an error condition.
Loading history...
546
        }
547
548
        if ($this->_lastInsertID !== false) {
549
            return $this->_lastInsertID;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->_lastInsertID; (false) is incompatible with the return type of the parent method yii\db\Schema::getLastInsertID of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
550
        }
551
        return null;
552
    }
553
}
554