Completed
Push — master ( 07fa0a...a5b00e )
by Edgard
10:14
created

Schema   C

Complexity

Total Complexity 70

Size/Duplication

Total Lines 516
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 92.28%

Importance

Changes 0
Metric Value
wmc 70
lcom 1
cbo 12
dl 0
loc 516
ccs 239
cts 259
cp 0.9228
rs 5.4782
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A createQueryBuilder() 0 4 1
A createColumnSchemaBuilder() 0 4 1
A quoteSimpleTableName() 0 13 4
A quoteSimpleColumnName() 0 7 2
A loadTableSchema() 0 10 2
A getPdoType() 0 14 2
A resolveTableNames() 0 12 2
C findColumns() 0 74 11
A createColumnSchema() 0 4 1
F loadColumnSchema() 0 123 21
B findConstraints() 0 39 4
A findTableNames() 0 19 2
A findUniqueIndexes() 0 17 2
A setTransactionIsolationLevel() 0 12 4
C insert() 0 32 7
A getLastInsertID() 0 15 4

How to fix   Complexity   

Complex Class

Complex classes like Schema 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Schema, and based on these observations, apply Extract Interface, too.

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 94
    public function createQueryBuilder()
72
    {
73 94
        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 147
    public function quoteSimpleTableName($name)
85
    {
86 147
        if ($this->db->tablePrefix !== '') {
87
            return $name;
88
        }
89
90 147
        $word = strtoupper(str_replace('%', '', $name));
91 147
        if (in_array($word, $this->reservedWords)) {
92 41
            return strpos($name, '"') !== false ? $name : '"' . $name . '"';
93
        }
94
95 146
        return $name;
96
    }
97
98 167
    public function quoteSimpleColumnName($name)
99
    {
100 167
        if (in_array(strtoupper($name), $this->reservedWords)) {
101 6
            return parent::quoteSimpleColumnName($name);
102
        }
103 167
        return $name;
104
    }
105
106 98
    protected function loadTableSchema($name)
107
    {
108 98
        $table = new TableSchema;
109 98
        $this->resolveTableNames($table, $name);
110 98
        if ($this->findColumns($table)) {
111 96
            $this->findConstraints($table);
112 96
            return $table;
113
        }
114 7
        return null;
115
    }
116
117 90
    public function getPdoType($data)
118
    {
119
        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 90
        ];
127 90
        $type = gettype($data);
128
129 90
        return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
130
    }
131
132
    /**
133
     *
134
     * @param TableSchema $table
135
     * @param string $name
136
     */
137 98
    protected function resolveTableNames($table, $name)
138
    {
139 98
        $parts = explode('.', str_replace('"', '', $name));
140 98
        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 98
            $table->name = strtolower($parts[0]);
146 98
            $table->fullName = $this->quoteTableName($table->name);
147
        }
148 98
    }
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 98
    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
                    (SELECT RDB$TRIGGER_SOURCE FROM RDB$TRIGGERS
173
                        WHERE RDB$SYSTEM_FLAG = 0
174 98
                        AND UPPER(RDB$RELATION_NAME)=UPPER(\'' . $table->name . '\')
175
                        AND RDB$TRIGGER_TYPE = 1
176
                        AND RDB$TRIGGER_INACTIVE = 0
177
                        AND (UPPER(REPLACE(RDB$TRIGGER_SOURCE,\' \',\'\')) LIKE \'%NEW.\'||TRIM(rel.rdb$field_name)||\'=GEN_ID%\'
178
                            OR UPPER(REPLACE(RDB$TRIGGER_SOURCE,\' \',\'\')) LIKE \'%NEW.\'||TRIM(rel.rdb$field_name)||\'=NEXTVALUEFOR%\'))
179
                    AS fautoinc
180
                FROM
181
                    rdb$relation_fields rel
182
                    JOIN rdb$fields fld ON rel.rdb$field_source=fld.rdb$field_name
183
                WHERE
184 98
                    UPPER(rel.rdb$relation_name)=UPPER(\'' . $table->name . '\')
185
                ORDER BY
186 98
                    rel.rdb$field_position;';
187
        try {
188 98
            $columns = $this->db->createCommand($sql)->queryAll();
189 98
            if (empty($columns)) {
190 7
                return false;
191
            }
192 96
        } catch (Exception $e) {
193
            return false;
194
        }
195
        $sql = 'SELECT
196
                    idx.rdb$field_name AS fname
197
                FROM
198
                    rdb$relation_constraints rc
199
                    JOIN rdb$index_segments idx ON idx.rdb$index_name=rc.rdb$index_name
200
                WHERE rc.rdb$constraint_type=\'PRIMARY KEY\'
201 96
					AND UPPER(rc.rdb$relation_name)=UPPER(\'' . $table->name . '\')';
202
        try {
203 96
            $pkeys = $this->db->createCommand($sql)->queryColumn();
204 96
        } catch (Exception $e) {
205
            return false;
206
        }
207 96
        $pkeys = array_map("rtrim", $pkeys);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal rtrim does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
208 96
        $pkeys = array_map("strtolower", $pkeys);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal strtolower does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
209 96
        foreach ($columns as $key => $column) {
210 96
            $column = array_map("strtolower", $column);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal strtolower does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
211 96
            $columns[$key]['fprimary'] = in_array(rtrim($column['fname']), $pkeys);
212 96
        }
213 96
        foreach ($columns as $column) {
214 96
            $c = $this->loadColumnSchema($column);
215 96
            if ($table->sequenceName === null && $c->autoIncrement) {
216 81
                $matches = [];
217 81
                if (preg_match("/NEW.{$c->name}\s*=\s*GEN_ID\((\w+)/i", $column['fautoinc'], $matches)) {
218 73
                    $table->sequenceName = $matches[1];
219 81
                } elseif (preg_match("/NEW.{$c->name}\s*=\s*NEXT\s+VALUE\s+FOR\s+(\w+)/i", $column['fautoinc'], $matches)) {
220 13
                    $table->sequenceName = $matches[1];
221 13
                }
222 81
            }
223 96
            $table->columns[$c->name] = $c;
224 96
            if ($c->isPrimaryKey) {
225 85
                $table->primaryKey[] = $c->name;
226 85
            }
227 96
        }
228 96
        return (count($table->columns) > 0);
229
    }
230
231
    /**
232
     * @return ColumnSchema
233
     * @throws \yii\base\InvalidConfigException
234
     */
235 96
    protected function createColumnSchema()
236
    {
237 96
        return \Yii::createObject('\edgardmessias\db\firebird\ColumnSchema');
238
    }
239
240
    /**
241
     * Creates a table column.
242
     *
243
     * @param array $column column metadata
244
     * @return ColumnSchema normalized column metadata
245
     */
246 96
    protected function loadColumnSchema($column)
247
    {
248 96
        $c = $this->createColumnSchema();
249 96
        $c->name = strtolower(rtrim($column['fname']));
250 96
        $c->allowNull = $column['fnull'] !== '1';
251 96
        $c->isPrimaryKey = $column['fprimary'];
252 96
        $c->autoIncrement = (boolean) $column['fautoinc'];
253 96
        $c->comment = $column['fcomment'] === null ? '' : $column['fcomment'];
254
255 96
        $c->type = self::TYPE_STRING;
256
257 96
        $defaultValue = null;
258 96
        if (!empty($column['fdefault'])) {
259
            // remove whitespace, 'DEFAULT ' prefix and surrounding single quotes; all optional
260 57
            if (preg_match("/\s*(DEFAULT\s+){0,1}('(.*)'|(.*))\s*/i", $column['fdefault'], $parts)) {
261 57
                $defaultValue = array_pop($parts);
262 57
            }
263
            // handle escaped single quotes like in "funny''quoted''string"
264 57
            $defaultValue = str_replace('\'\'', '\'', $defaultValue);
265 57
        }
266 96
        if ($defaultValue === null) {
267 95
            $defaultValue = $column['fdefault_value'];
268 95
        }
269 96
        $dbType = "";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
270
        $baseTypes = [
271 96
            7   => 'SMALLINT',
272 96
            8   => 'INTEGER',
273 96
            16  => 'INT64',
274 96
            9   => 'QUAD',
275 96
            10  => 'FLOAT',
276 96
            11  => 'D_FLOAT',
277 96
            17  => 'BOOLEAN',
278 96
            27  => 'DOUBLE PRECISION',
279 96
            12  => 'DATE',
280 96
            13  => 'TIME',
281 96
            35  => 'TIMESTAMP',
282 96
            261 => 'BLOB',
283 96
            40  => 'CSTRING',
284 96
            45  => 'BLOB_ID',
285 96
        ];
286
        $baseCharTypes = [
287 96
            37 => 'VARCHAR',
288 96
            14 => 'CHAR',
289 96
        ];
290 96
        if (array_key_exists((int) $column['fcodtype'], $baseTypes)) {
291 96
            $dbType = $baseTypes[(int) $column['fcodtype']];
292 96
        } elseif (array_key_exists((int) $column['fcodtype'], $baseCharTypes)) {
293 80
            $c->size = (int) $column['fcharlength'];
294 80
            $c->precision = $c->size;
295 80
            $dbType = $baseCharTypes[(int) $column['fcodtype']] . "($c->size)";
296 80
        }
297 96
        switch ((int) $column['fcodtype']) {
298 96
            case 7:
299 96
            case 8:
300 96
                switch ((int) $column['fcodsubtype']) {
301 96
                    case 1:
302
                        $c->precision = (int) $column['fprecision'];
303
                        $c->size = $c->precision;
304
                        $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...
305
                        $dbType = "NUMERIC({$c->precision},{$c->scale})";
306
                        break;
307 96
                    case 2:
308 13
                        $c->precision = (int) $column['fprecision'];
309 13
                        $c->size = $c->precision;
310 13
                        $c->scale = abs((int) $column['fscale']);
311 13
                        $dbType = "DECIMAL({$c->precision},{$c->scale})";
312 13
                        break;
313
                }
314 96
                break;
315 93
            case 16:
316 46
                switch ((int) $column['fcodsubtype']) {
317 46
                    case 1:
318 3
                        $c->precision = (int) $column['fprecision'];
319 3
                        $c->size = $c->precision;
320 3
                        $c->scale = abs((int) $column['fscale']);
321 3
                        $dbType = "NUMERIC({$c->precision},{$c->scale})";
322 3
                        break;
323 46
                    case 2:
324 45
                        $c->precision = (int) $column['fprecision'];
325 45
                        $c->size = $c->precision;
326 45
                        $c->scale = abs((int) $column['fscale']);
327 45
                        $dbType = "DECIMAL({$c->precision},{$c->scale})";
328 45
                        break;
329 4
                    default:
330 4
                        $dbType = 'BIGINT';
331 4
                        break;
332 4
                }
333 46
                break;
334 81
            case 261:
335 3
                switch ((int) $column['fcodsubtype']) {
336 3
                    case 1:
337 3
                        $dbType = 'BLOB SUB_TYPE TEXT';
338 3
                        $c->size = null;
339 3
                        break;
340
                }
341 3
                break;
342
        }
343
344 96
        $c->dbType = strtolower($dbType);
345
346 96
        $c->type = self::TYPE_STRING;
347 96
        if (preg_match('/^([\w\ ]+)(?:\(([^\)]+)\))?/', $c->dbType, $matches)) {
348 96
            $type = strtolower($matches[1]);
349 96
            if (isset($this->typeMap[$type])) {
350 96
                $c->type = $this->typeMap[$type];
351 96
            }
352 96
        }
353
354
355 96
        $c->phpType = $this->getColumnPhpType($c);
356
357 96
        $c->defaultValue = null;
358 96
        if ($defaultValue !== null) {
359 57
            if (in_array($c->type, [self::TYPE_DATE, self::TYPE_DATETIME, self::TYPE_TIME, self::TYPE_TIMESTAMP])
360 57
                    && preg_match('/(CURRENT_|NOW|NULL|TODAY|TOMORROW|YESTERDAY)/i', $defaultValue)) {
361 12
                $c->defaultValue = new \yii\db\Expression(trim($defaultValue));
362 12
            } else {
363 57
                $c->defaultValue = $c->phpTypecast($defaultValue);
364
            }
365 57
        }
366
367 96
        return $c;
368
    }
369
370
    /**
371
     * Collects the foreign key column details for the given table.
372
     *
373
     * @param TableSchema $table the table metadata
374
     */
375 96
    protected function findConstraints($table)
376
    {
377
        // Zoggo - Converted sql to use join syntax
378
        $sql = 'SELECT
379
                    a.rdb$constraint_name as fconstraint,
380
                    c.rdb$relation_name AS ftable,
381
                    d.rdb$field_name AS pfield,
382
                    e.rdb$field_name AS ffield
383
                FROM
384
                    rdb$ref_constraints b
385
                    JOIN rdb$relation_constraints a ON a.rdb$constraint_name=b.rdb$constraint_name
386
                    JOIN rdb$relation_constraints c ON b.rdb$const_name_uq=c.rdb$constraint_name
387
                    JOIN rdb$index_segments d ON c.rdb$index_name=d.rdb$index_name
388
                    JOIN rdb$index_segments e ON a.rdb$index_name=e.rdb$index_name AND e.rdb$field_position = d.rdb$field_position
389
                WHERE
390
                    a.rdb$constraint_type=\'FOREIGN KEY\' AND
391 96
                    UPPER(a.rdb$relation_name)=UPPER(\'' . $table->name . '\') ';
392
        try {
393 96
            $fkeys = $this->db->createCommand($sql)->queryAll();
394 96
        } catch (Exception $e) {
395
            return false;
396
        }
397
398 96
        $constraints = [];
399 96
        foreach ($fkeys as $fkey) {
400
            // Zoggo - Added strtolower here to guarantee that values are
401
            // returned lower case. Otherwise gii generates wrong code.
402 29
            $fkey = array_map("rtrim", $fkey);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal rtrim does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
403 29
            $fkey = array_map("strtolower", $fkey);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal strtolower does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
404
405 29
            if (!isset($constraints[$fkey['fconstraint']])) {
406 29
                $constraints[$fkey['fconstraint']] = [
407 29
                    $fkey['ftable']
408 29
                ];
409 29
            }
410 29
            $constraints[$fkey['fconstraint']][$fkey['ffield']] = $fkey['pfield'];
411 96
        }
412 96
        $table->foreignKeys = array_values($constraints);
413 96
    }
414
415 5
    protected function findTableNames($schema = '')
416
    {
417
        $sql = 'SELECT
418
                    rdb$relation_name
419
                FROM
420
                    rdb$relations
421
                WHERE
422 5
                    (rdb$system_flag is null OR rdb$system_flag=0)';
423
        try {
424 5
            $tables = $this->db->createCommand($sql)->queryColumn();
425 5
        } catch (Exception $e) {
426
            return false;
427
        }
428
429 5
        $tables = array_map('rtrim', $tables);
430 5
        $tables = array_map('strtolower', $tables);
431
432 5
        return $tables;
433
    }
434
435
    /**
436
     * Returns all unique indexes for the given table.
437
     * Each array element is of the following structure:
438
     *
439
     * ~~~
440
     * [
441
     *  'IndexName1' => ['col1' [, ...]],
442
     *  'IndexName2' => ['col2' [, ...]],
443
     * ]
444
     * ~~~
445
     *
446
     * @param TableSchema $table the table metadata
447
     * @return array all unique indexes for the given table.
448
     * @since 2.0.4
449
     */
450 1
    public function findUniqueIndexes($table)
451
    {
452
        $query = '
453
SELECT id.RDB$INDEX_NAME as index_name, ids.RDB$FIELD_NAME as column_name
454
FROM RDB$INDICES id
455
INNER JOIN RDB$INDEX_SEGMENTS ids ON ids.RDB$INDEX_NAME = id.RDB$INDEX_NAME
456
WHERE id.RDB$UNIQUE_FLAG = 1
457
AND   id.RDB$SYSTEM_FLAG = 0
458 1
AND UPPER(id.RDB$RELATION_NAME) = UPPER(\'' . $table->name . '\')
459 1
ORDER BY id.RDB$RELATION_NAME, id.RDB$INDEX_NAME, ids.RDB$FIELD_POSITION';
460 1
        $result = [];
461 1
        $command = $this->db->createCommand($query);
462 1
        foreach ($command->queryAll() as $row) {
463 1
            $result[strtolower(rtrim($row['index_name']))][] = strtolower(rtrim($row['column_name']));
464 1
        }
465 1
        return $result;
466
    }
467
468
    /**
469
     * Sets the isolation level of the current transaction.
470
     * @param string $level The transaction isolation level to use for this transaction.
471
     * This can be one of [[Transaction::READ_UNCOMMITTED]], [[Transaction::READ_COMMITTED]], [[Transaction::REPEATABLE_READ]]
472
     * and [[Transaction::SERIALIZABLE]] but also a string containing DBMS specific syntax to be used
473
     * after `SET TRANSACTION ISOLATION LEVEL`.
474
     * @see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels
475
     */
476 2
    public function setTransactionIsolationLevel($level)
477
    {
478 2
        if ($level == \yii\db\Transaction::READ_UNCOMMITTED) {
479 2
            parent::setTransactionIsolationLevel('READ COMMITTED RECORD_VERSION');
480 2
        } elseif ($level == \yii\db\Transaction::REPEATABLE_READ) {
481 1
            parent::setTransactionIsolationLevel('SNAPSHOT');
482 1
        } elseif ($level == \yii\db\Transaction::SERIALIZABLE) {
483 1
            parent::setTransactionIsolationLevel('SNAPSHOT TABLE STABILITY');
484 1
        } else {
485 1
            parent::setTransactionIsolationLevel($level);
486
        }
487 2
    }
488
489
    /**
490
     * @inheritdoc
491
     */
492 13
    public function insert($table, $columns)
493
    {
494 13
        $this->_lastInsertID = false;
495 13
        $params = [];
496 13
        $sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
497 13
        $returnColumns = $this->getTableSchema($table)->primaryKey;
498 13
        if (!empty($returnColumns)) {
499 12
            $returning = [];
500 12
            foreach ((array) $returnColumns as $name) {
501 12
                $returning[] = $this->quoteColumnName($name);
502 12
            }
503 12
            $sql .= ' RETURNING ' . implode(', ', $returning);
504 12
        }
505
506 13
        $command = $this->db->createCommand($sql, $params);
507 13
        $command->prepare(false);
508 13
        $result = $command->queryOne();
509
510 13
        if (!$command->pdoStatement->rowCount()) {
511
            return false;
512
        } else {
513 13
            if (!empty($returnColumns)) {
514 12
                foreach ((array) $returnColumns as $name) {
515 12
                    if ($this->getTableSchema($table)->getColumn($name)->autoIncrement) {
516 10
                        $this->_lastInsertID = $result[$name];
517 10
                        break;
518
                    }
519 12
                }
520 12
            }
521 13
            return $result;
522
        }
523
    }
524
525
    /**
526
     * @inheritdoc
527
     */
528 2
    public function getLastInsertID($sequenceName = '')
529
    {
530 2
        if (!$this->db->isActive) {
531
            throw new InvalidCallException('DB Connection is not active.');
532
        }
533
        
534 2
        if ($sequenceName !== '') {
535 2
            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 535 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...
536
        }
537
538 1
        if ($this->_lastInsertID !== false) {
539 1
            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...
540
        }
541
        return null;
542
    }
543
}
544