Completed
Push — master ( 767c34...79d01e )
by Todd
02:06
created

Db::nativeDbType()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1
ccs 0
cts 0
cp 0
c 0
b 0
f 0
nc 1
1
<?php
2
/**
3
 * @author Todd Burry <[email protected]>
4
 * @copyright 2009-2014 Vanilla Forums Inc.
5
 * @license MIT
6
 */
7
8
namespace Garden\Db;
9
10
use PDO;
11
12
/**
13
 * Defines a standard set of methods that all database drivers must conform to.
14
 */
15
abstract class Db {
16
    use Utils\FetchModeTrait;
17
18
    const QUERY_DEFINE = 'define';
19
    const QUERY_READ = 'read';
1 ignored issue
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
20
    const QUERY_WRITE = 'write';
1 ignored issue
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
21
22
    const INDEX_PK = 'primary';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
23
    const INDEX_IX = 'index';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
24
    const INDEX_UNIQUE = 'unique';
25
26
    const OPTION_REPLACE = 'replace';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
27
    const OPTION_IGNORE = 'ignore';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
28
    const OPTION_UPSERT = 'upsert';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
29
    const OPTION_TRUNCATE = 'truncate';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
30
    const OPTION_DROP = 'drop';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
31
    const OPTION_FETCH_MODE = 'fetchMode';
32
33
    const OP_EQ = '=';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
34
    const OP_GT = '>';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
35
    const OP_GTE = '>=';
1 ignored issue
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
36
    const OP_IN = '$in';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
37
    const OP_LIKE = '$like';
38
    const OP_LT = '<';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
39
    const OP_LTE = '<=';
1 ignored issue
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
40
    const OP_NEQ = '<>';
1 ignored issue
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
41
42
    const OP_AND = '$and';
43
    const OP_OR = '$or';
1 ignored issue
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
44
45
    /**
46
     * @var string[] Maps PDO drivers to db classes.
47
     */
48
    private static $drivers = [
49
        'mysql' => MySqlDb::class,
50
        'sqlite' => SqliteDb::class
51
    ];
52
53
    /**
54
     * @var array The canonical database types.
55
     */
56
    private static $types = [
57
        // String
58
        'char' => ['type' => 'string', 'length' => true],
59
        'varchar' => ['type' => 'string', 'length' => true],
60
        'tinytext' => ['type' => 'string', 'schema' => ['maxLength' => 255]],
61
        'text' => ['type' => 'string', 'schema' => ['maxLength' =>  65535]],
62
        'mediumtext' => ['type' => 'string', 'schema' => ['maxLength' => 16777215]],
63
        'longtext' => ['type' => 'string', 'schema' => ['maxLength' => 4294967295]],
64
        'binary' => ['type' => 'string', 'length' => true],
65
        'varbinary' => ['type' => 'string', 'length' => true],
66
67
        // Boolean
68
        'bool' => ['type' => 'boolean'],
69
70
        // Integer
71
        'byte' => ['type' => 'integer', 'schema' => ['maximum' => 127, 'minimum' => -128]],
72
        'short' => ['type' => 'integer', 'schema' => ['maximum' => 32767, 'minimum' => -32768]],
73
        'int' => ['type' => 'integer', 'schema' => ['maximum' => 2147483647, 'minimum' => -2147483648]],
74
        'long' => ['type' => 'integer'],
75
76
        // Number
77
        'float' => ['type' => 'number'],
78
        'double' => ['type' => 'number'],
79
        'decimal' => ['type' => 'number', 'precision' => true],
80
        'numeric' => ['type' => 'number', 'precision' => true],
81
82
        // Date/Time
83
        'datetime' => ['type' => 'datetime'],
84
        'timestamp' => ['type' => 'datetime'],
85
86
        // Enum
87
        'enum' => ['type' => 'string', 'enum' => true],
88
89
        // Schema types
90
        'string' => 'varchar',
91
        'boolean' => 'bool',
92
        'integer' => 'int',
93
        'number' => 'float',
94
95
        // Other aliases
96
        'character' => 'char',
97
        'tinyint' => 'byte',
98
        'int8' => 'byte',
99
        'smallint' => 'short',
100
        'int16' => 'short',
101
        'int32' => 'int',
102
        'bigint' => 'long',
103
        'int64' => 'long',
104
        'real' => 'double'
105
    ];
106
107
    /**
108
     * @var string The database prefix.
109
     */
110
    private $px = '';
111
112
    /**
113
     * @var array A cached copy of the table schemas indexed by lowercase name.
114
     */
115
    private $tables = [];
116
117
    /**
118
     * @var array|null A cached copy of the table names indexed by lowercase name.
119
     */
120
    private $tableNames = null;
121
122
    /**
123
     * @var \PDO
124
     */
125
    private $pdo;
126
127
    /**
128
     * Initialize an instance of the {@link MySqlDb} class.
129
     *
130
     * @param PDO $pdo The connection to the database.
131
     * @param string $px The database prefix.
132
     */
133
    public function __construct(PDO $pdo, $px = '') {
134
        $this->pdo = $pdo;
135
        $this->px = $px;
1 ignored issue
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
136
137
        $fetchMode = $this->pdo->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE);
138
        $this->setFetchMode(in_array($fetchMode, [0, PDO::FETCH_BOTH], true) ? PDO::FETCH_ASSOC: $fetchMode);
139
    }
140
141
    /**
142
     * Get the name of the class that handles a database driver.
143
     *
144
     * @param string|PDO $driver The name of the driver or a database connection.
145
     * @return null|string Returns the driver classname or **null** if one isn't found.
146
     */
147
    public static function driverClass($driver) {
148
        if ($driver instanceof PDO) {
149
            $name = $driver->getAttribute(PDO::ATTR_DRIVER_NAME);
150
        } else {
151
            $name = (string)$driver;
152
        }
153
154
        $name = strtolower($name);
155
        return isset(self::$drivers[$name]) ? self::$drivers[$name] : null;
156
    }
157
158
    /**
159
     * Add a table to the database.
160
     *
161
     * @param array $tableDef The table definition.
162
     * @param array $options An array of additional options when adding the table.
163
     */
164
    abstract protected function createTableDb(array $tableDef, array $options = []);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
165
166
    /**
167
     * Alter a table in the database.
168
     *
169
     * When altering a table you pass an array with three optional keys: add, drop, and alter.
170
     * Each value is consists of a table definition in a format that would be passed to {@link Db::setTableDef()}.
171
     *
172
     * @param array $alterDef The alter definition.
173
     * @param array $options An array of additional options when adding the table.
174
     */
175
    abstract protected function alterTableDb(array $alterDef, array $options = []);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
176
177
    /**
178
     * Drop a table.
179
     *
180
     * @param string $table The name of the table to drop.
181
     * @param array $options An array of additional options when adding the table.
182
     */
183 6
    final public function dropTable($table, array $options = []) {
184 6
        $options += [Db::OPTION_IGNORE => false];
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
185 6
        $this->dropTableDb($table, $options);
186
187 6
        $tableKey = strtolower($table);
188 6
        unset($this->tables[$tableKey], $this->tableNames[$tableKey]);
189 6
    }
190
191
    /**
192
     * Perform the actual table drop.
193
     *
194
     * @param string $table The name of the table to drop.
195
     * @param array $options An array of additional options when adding the table.
196
     */
197
    abstract protected function dropTableDb($table, array $options = []);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
198
199
    /**
200
     * Get the names of all the tables in the database.
201
     *
202
     * @return string[] Returns an array of table names without prefixes.
203
     */
204
    final public function fetchTableNames() {
205
        if ($this->tableNames !== null) {
206
            return array_values($this->tableNames);
207
        }
208
209
        $names = $this->fetchTableNamesDb();
210
211
        $this->tableNames = [];
212
        foreach ($names as $name) {
213
            $name = $this->stripPrefix($name);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 32 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
214
            $this->tableNames[strtolower($name)] = $name;
215
        }
216
217
        return array_values($this->tableNames);
218
    }
219
220
    /**
221
     * Fetch the table names from the underlying database layer.
222
     *
223
     * The driver should return all table names. It doesn't have to strip the prefix.
224
     *
225
     * @return string[]
226
     */
227
    abstract protected function fetchTableNamesDb();
228
229
    /**
230
     * Get a table definition.
231
     *
232
     * @param string $table The name of the table.
233
     * @return array|null Returns the table definition or null if the table does not exist.
234
     */
235 64
    final public function fetchTableDef($table) {
236 64
        $tableKey = strtolower($table);
237
238
        // First check the table cache.
239 64
        if (isset($this->tables[$tableKey])) {
240 50
            $tableDef = $this->tables[$tableKey];
241
242 50
            if (isset($tableDef['columns'], $tableDef['indexes'])) {
243 50
                return $tableDef;
244
            }
245 28
        } elseif ($this->tableNames !== null && !isset($this->tableNames[$tableKey])) {
246 22
            return null;
247
        }
248
249 8
        $tableDef = $this->fetchTableDefDb($table);
250 8
        if ($tableDef !== null) {
251 8
            $this->fixIndexes($tableDef['name'], $tableDef);
252 8
            $this->tables[$tableKey] = $tableDef;
253
        }
254
255 8
        return $tableDef;
256
    }
257
258
    /**
259
     * Fetch the table definition from the database.
260
     *
261
     * @param string $table The name of the table to get.
262
     * @return array|null Returns the table def or **null** if the table doesn't exist.
263
     */
264
    abstract protected function fetchTableDefDb($table);
265
266
267
    /**
268
     * Get the column definitions for a table.
269
     *
270
     * @param string $table The name of the table to get the columns for.
271
     * @return array|null Returns an array of column definitions.
272
     */
273 1
    final public function fetchColumnDefs($table) {
274 1
        $tableKey = strtolower($table);
275
276 1
        if (!empty($this->tables[$tableKey]['columns'])) {
277 1
            $this->tables[$tableKey]['columns'];
278
        } elseif ($this->tableNames !== null && !isset($this->tableNames[$tableKey])) {
279
            return null;
280
        }
281
282 1
        $columnDefs = $this->fetchColumnDefsDb($table);
283 1
        if ($columnDefs !== null) {
284 1
            $this->tables[$tableKey]['columns'] = $columnDefs;
285
        }
286 1
        return $columnDefs;
287
    }
288
289
    /**
290
     * Get the column definitions from the database.
291
     *
292
     * @param string $table The name of the table to fetch the columns for.
293
     * @return array|null
294
     */
295
    abstract protected function fetchColumnDefsDb($table);
296
297
    /**
298
     * Get the canonical type based on a type string.
299
     *
300
     * @param string $type A type string.
301
     * @return array|null Returns the type schema array or **null** if a type isn't found.
302
     */
303 49
    public static function typeDef($type) {
304
        // Check for the unsigned signifier.
305 49
        $unsigned = null;
306 49
        if ($type[0] === 'u') {
307 6
            $unsigned = true;
308 6
            $type = substr($type, 1);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
309 47
        } elseif (preg_match('`(.+)\s+unsigned`i', $type, $m)) {
310 2
            $unsigned = true;
311 2
            $type = $m[1];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
312
        }
313
314
        // Remove brackets from the type.
315 49
        $brackets = null;
316 49
        if (preg_match('`^(.*)\((.*)\)$`', $type, $m)) {
317 33
            $brackets = $m[2];
318 33
            $type = $m[1];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
319
        }
320
321
        // Look for the type.
322 49
        $type = strtolower($type);
323 49
        if (isset(self::$types[$type])) {
324 49
            $row = self::$types[$type];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
325 49
            $dbtype = $type;
326
327
            // Resolve an alias.
328 49
            if (is_string($row)) {
329 2
                $dbtype = $row;
330 2
                $row = self::$types[$row];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
331
            }
332
        } else {
333
            return null;
334
        }
335
336
        // Now that we have a type row we can build a schema for it.
337
        $schema = [
338 49
            'type' => $row['type'],
339 49
            'dbtype' => $dbtype
340
        ];
341
342 49
        if (!empty($row['schema'])) {
343 40
            $schema += $row['schema'];
344
        }
345
346 49
        if ($row['type'] === 'integer' && $unsigned) {
347 6
            $schema['unsigned'] = true;
348
349 6
            if (!empty($schema['maximum'])) {
350 6
                $schema['maximum'] = $schema['maximum'] * 2 + 1;
351 6
                $schema['minimum'] = 0;
352
            }
353
        }
354
355 49
        if (!empty($row['length'])) {
356 26
            $schema['maxLength'] = (int)$brackets ?: 255;
357
        }
358
359 49
        if (!empty($row['precision'])) {
360 2
            $parts = array_map('trim', explode(',', $brackets));
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 15 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
361 2
            $schema['precision'] = (int)$parts[0];
362 2
            if (isset($parts[1])) {
363 2
                $schema['scale'] = (int)$parts[1];
364
            }
365
        }
366
367 49
        if (!empty($row['enum'])) {
368 1
            $enum = explode(',', $brackets);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 11 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
369
            $schema['enum'] = array_map(function ($str) {
370 1
                return trim($str, "'\" \t\n\r\0\x0B");
371 1
            }, $enum);
372
        }
373
374 49
        return $schema;
375
    }
376
377
    /**
378
     * Get the database type string from a type definition.
379
     *
380
     * This is the opposite of {@link Db::typeDef()}.
381
     *
382
     * @param array $typeDef The type definition array.
383
     * @return string Returns a db type string.
384
     */
385 28
    protected static function dbType(array $typeDef) {
386 28
        $dbtype = $typeDef['dbtype'];
387
388 28
        if (!empty($typeDef['maxLength'])) {
389 14
            $dbtype .= "({$typeDef['maxLength']})";
390 28
        } elseif (!empty($typeDef['unsigned'])) {
391
            $dbtype = 'u'.$dbtype;
392 28
        } elseif (!empty($typeDef['precision'])) {
393
            $dbtype .= "({$typeDef['precision']}";
394
            if (!empty($typeDef['scale'])) {
395
                $dbtype .= ",{$typeDef['scale']}";
396
            }
397
            $dbtype .= ')';
398 28
        } elseif (!empty($typeDef['enum'])) {
399
            $parts = array_map(function ($str) {
400
                return "'{$str}'";
401
            }, $typeDef['enum']);
402
            $dbtype .= '('.implode(',', $parts).')';
403
        }
404 28
        return $dbtype;
405
    }
406
407
408
    /**
409
     * Get the native database type based on a type schema.
410
     *
411
     * The default implementation of this method returns the canonical db types. Individual database classes will have
412
     * to override to provide any differences.
413
     *
414
     * @param array $type The type schema.
415
     * @return string
416
     */
417
    abstract protected function nativeDbType(array $type);
418
419
    /**
420
     * Set a table definition to the database.
421
     *
422
     * @param array $tableDef The table definition.
423
     * @param array $options An array of additional options when adding the table.
424
     */
425 64
    final public function defineTable(array $tableDef, array $options = []) {
426 64
        $options += [Db::OPTION_DROP => false];
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
427
428 64
        $tableName = $tableDef['name'];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 8 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
429 64
        $tableKey = strtolower($tableName);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
430 64
        $tableDef['name'] = $tableName;
431 64
        $curTable = $this->fetchTableDef($tableName);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
432
433 64
        $this->fixIndexes($tableName, $tableDef, $curTable);
434
435 64
        if (!$curTable) {
436 28
            $this->createTableDb($tableDef, $options);
437 28
            $this->tables[$tableKey] = $tableDef;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
438 28
            $this->tableNames[$tableKey] = $tableDef['name'];
439 28
            return;
440
        }
441
        // This is the alter statement.
442 45
        $alterDef = ['name' => $tableName];
443
444
        // Figure out the columns that have changed.
445 45
        $curColumns = (array)$curTable['columns'];
446 45
        $newColumns = (array)$tableDef['columns'];
447
448 45
        $alterDef['add']['columns'] = array_diff_key($newColumns, $curColumns);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
449 45
        $alterDef['alter']['columns'] = array_uintersect_assoc($newColumns, $curColumns, function ($new, $curr) {
450 45
            $search = ['dbtype', 'allowNull', 'default'];
451 45
            foreach ($search as $key) {
452 45
                if (self::val($key, $curr) !== self::val($key, $new)) {
453
                    // Return 0 if the values are different, not the same.
454 4
                    return 0;
455
                }
456
            }
457 45
            return 1;
458 45
        });
459
460
        // Figure out the indexes that have changed.
461 45
        $curIndexes = (array)self::val('indexes', $curTable, []);
462 45
        $newIndexes = (array)self::val('indexes', $tableDef, []);
463
464 45
        $alterDef['add']['indexes'] = array_udiff($newIndexes, $curIndexes, [$this, 'indexCompare']);
465
466 45
        $dropIndexes = array_udiff($curIndexes, $newIndexes, [$this, 'indexCompare']);
467 45
        if ($options[Db::OPTION_DROP]) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
468 2
            $alterDef['drop']['columns'] = array_diff_key($curColumns, $newColumns);
469 2
            $alterDef['drop']['indexes'] = $dropIndexes;
470
        } else {
471 43
            $alterDef['drop']['columns'] = [];
472 43
            $alterDef['drop']['indexes'] = [];
473
474
            // If the primary key has changed then the old one needs to be dropped.
475 43
            if ($pk = $this->findPrimaryKeyIndex($dropIndexes)) {
476 4
                $alterDef['drop']['indexes'][] = $pk;
477
            }
478
        }
479
480
        // Check to see if any alterations at all need to be made.
481 45
        if (empty($alterDef['add']['columns']) && empty($alterDef['add']['indexes']) &&
482 37
            empty($alterDef['drop']['columns']) && empty($alterDef['drop']['indexes']) &&
483 37
            empty($alterDef['alter']['columns'])
484
        ) {
485 37
            return;
486
        }
487
488 8
        $alterDef['def'] = $tableDef;
489
490
        // Alter the table.
491 8
        $this->alterTableDb($alterDef, $options);
492
493
        // Update the cached schema.
494 8
        $tableDef['name'] = $tableName;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 12 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
495 8
        $this->tables[$tableKey] = $tableDef;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
496 8
        $this->tableNames[$tableKey] = $tableName;
497 8
    }
498
499
    /**
500
     * Find the primary key in an array of indexes.
501
     *
502
     * @param array $indexes The indexes to search.
503
     * @return array|null Returns the primary key or **null** if there isn't one.
504
     */
505 51
    protected function findPrimaryKeyIndex(array $indexes) {
506 51
        foreach ($indexes as $index) {
507 13
            if ($index['type'] === Db::INDEX_PK) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
508 10
                return $index;
509
            }
510
        }
511 41
        return null;
512
    }
513
514
    /**
515
     * Move the primary key index into the correct place for database drivers.
516
     *
517
     * @param string $tableName The name of the table.
518
     * @param array &$tableDef The table definition.
519
     * @param array|null $curTableDef The current database table def used to resolve conflicts in some names.
520
     * @throws \Exception Throws an exception when there is a mismatch between the primary index and the primary key
521
     * defined on the columns themselves.
522
     */
523 64
    private function fixIndexes($tableName, array &$tableDef, $curTableDef = null) {
524 64
        $tableDef += ['indexes' => []];
525
526
        // Loop through the columns and add the primary key index.
527 64
        $primaryColumns = [];
528 64
        foreach ($tableDef['columns'] as $cname => $cdef) {
529 64
            if (!empty($cdef['primary'])) {
530 26
                $primaryColumns[] = $cname;
531
            }
532
        }
533
534
        // Massage the primary key index.
535 64
        $primaryFound = false;
536 64
        foreach ($tableDef['indexes'] as &$indexDef) {
537 64
            $indexDef += ['name' => $this->buildIndexName($tableName, $indexDef), 'type' => null];
538
539 64
            if ($indexDef['type'] === Db::INDEX_PK) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
540 28
                $primaryFound = true;
541
542 28
                if (empty($primaryColumns)) {
543 8
                    foreach ($indexDef['columns'] as $cname) {
544 8
                        $tableDef['columns'][$cname]['primary'] = true;
545
                    }
546 22
                } elseif (array_diff($primaryColumns, $indexDef['columns'])) {
547
                    throw new \Exception("There is a mismatch in the primary key index and primary key columns.", 500);
548
                }
549 58
            } elseif (isset($curTableDef['indexes'])) {
550 41
                foreach ($curTableDef['indexes'] as $curIndexDef) {
551 41
                    if ($this->indexCompare($indexDef, $curIndexDef) === 0) {
552 41
                        if (!empty($curIndexDef['name'])) {
553 41
                            $indexDef['name'] = $curIndexDef['name'];
554
                        }
555 41
                        break;
556
                    }
557
                }
558
            }
559
        }
560
561 64
        if (!$primaryFound && !empty($primaryColumns)) {
562 4
            $tableDef['indexes'][] = [
563 4
                'columns' => $primaryColumns,
564
                'type' => Db::INDEX_PK
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
565
            ];
566
        }
567 64
    }
568
569
    /**
570
     * Get the database prefix.
571
     *
572
     * @return string Returns the current db prefix.
573
     */
574
    public function getPx() {
575
        return $this->px;
576
    }
577
578
    /**
579
     * Set the database prefix.
580
     *
581
     * @param string $px The new database prefix.
582
     */
583
    public function setPx($px) {
584
        $this->px = $px;
585
    }
586
587
    /**
588
     * Compare two index definitions to see if they have the same columns and same type.
589
     *
590
     * @param array $a The first index.
591
     * @param array $b The second index.
592
     * @return int Returns an integer less than, equal to, or greater than zero if {@link $a} is
593
     * considered to be respectively less than, equal to, or greater than {@link $b}.
594
     */
595 45
    private function indexCompare(array $a, array $b) {
596 45
        if ($a['columns'] > $b['columns']) {
597 15
            return 1;
598 45
        } elseif ($a['columns'] < $b['columns']) {
599 15
            return -1;
600
        }
601
602 41
        return strcmp(
603 41
            isset($a['type']) ? $a['type'] : '',
604 41
            isset($b['type']) ? $b['type'] : ''
605
        );
606
    }
607
608
    /**
609
     * Get data from the database.
610
     *
611
     * @param string|Identifier $table The name of the table to get the data from.
612
     * @param array $where An array of where conditions.
613
     * @param array $options An array of additional options.
614
     * @return \PDOStatement Returns the result set.
615
     */
616
    abstract public function get($table, array $where, array $options = []);
617
618
    /**
619
     * Get a single row from the database.
620
     *
621
     * This is a convenience method that calls {@link Db::get()} and shifts off the first row.
622
     *
623
     * @param string|Identifier $table The name of the table to get the data from.
624
     * @param array $where An array of where conditions.
625
     * @param array $options An array of additional options.
626
     * @return array|object|null Returns the row or false if there is no row.
627
     */
628 14
    final public function getOne($table, array $where, array $options = []) {
629 14
        $options['limit'] = 1;
630 14
        $rows = $this->get($table, $where, $options);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 13 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
631 14
        $row = $rows->fetch();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 14 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
632
633 14
        return $row === false ? null : $row;
634
    }
635
636
    /**
637
     * Insert a row into a table.
638
     *
639
     * @param string $table The name of the table to insert into.
640
     * @param array $row The row of data to insert.
641
     * @param array $options An array of options for the insert.
642
     *
643
     * Db::OPTION_IGNORE
644
     * : Whether or not to ignore inserts that lead to a duplicate key. *default false*
645
     * Db::OPTION_REPLACE
646
     * : Whether or not to replace duplicate keys. *default false*
647
     * Db::OPTION_UPSERT
648
     * : Whether or not to update the existing data when duplicate keys exist.
649
     *
650
     * @return mixed Returns the id of the inserted record, **true** if the table doesn't have an auto increment, or **false** otherwise.
651
     * @see Db::load()
652
     */
653
    abstract public function insert($table, array $row, array $options = []);
654
655
    /**
656
     * Load many rows into a table.
657
     *
658
     * @param string $table The name of the table to insert into.
659
     * @param \Traversable|array $rows A dataset to insert.
660
     * Note that all rows must contain the same columns.
661
     * The first row will be looked at for the structure of the insert and the rest of the rows will use this structure.
662
     * @param array $options An array of options for the inserts. See {@link Db::insert()} for details.
663
     * @see Db::insert()
664
     */
665
    public function load($table, $rows, array $options = []) {
666
        foreach ($rows as $row) {
667
            $this->insert($table, $row, $options);
668
        }
669
    }
670
671
672
    /**
673
     * Update a row or rows in a table.
674
     *
675
     * @param string $table The name of the table to update.
676
     * @param array $set The values to set.
677
     * @param array $where The where filter for the update.
678
     * @param array $options An array of options for the update.
679
     * @return int Returns the number of affected rows.
680
     */
681
    abstract public function update($table, array $set, array $where, array $options = []);
682
683
    /**
684
     * Delete rows from a table.
685
     *
686
     * @param string $table The name of the table to delete from.
687
     * @param array $where The where filter of the delete.
688
     * @param array $options An array of options.
689
     *
690
     * Db:OPTION_TRUNCATE
691
     * : Truncate the table instead of deleting rows. In this case {@link $where} must be blank.
692
     * @return int Returns the number of affected rows.
693
     */
694
    abstract public function delete($table, array $where, array $options = []);
695
696
    /**
697
     * Reset the internal table definition cache.
698
     *
699
     * @return $this
700
     */
701 8
    public function reset() {
702 8
        $this->tables = [];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
703 8
        $this->tableNames = null;
704 8
        return $this;
705
    }
706
707
    /**
708
     * Build a standardized index name from an index definition.
709
     *
710
     * @param string $tableName The name of the table the index is in.
711
     * @param array $indexDef The index definition.
712
     * @return string Returns the index name.
713
     */
714 64
    protected function buildIndexName($tableName, array $indexDef) {
715 64
        $indexDef += ['type' => Db::INDEX_IX, 'suffix' => ''];
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
716
717 64
        $type = $indexDef['type'];
718
719 64
        if ($type === Db::INDEX_PK) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
720 29
            return 'primary';
721
        }
722 58
        $px = self::val($type, [Db::INDEX_IX => 'ix_', Db::INDEX_UNIQUE => 'ux_'], 'ix_');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
723 58
        $sx = $indexDef['suffix'];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
724 58
        $result = $px.$tableName.'_'.($sx ?: implode('', $indexDef['columns']));
725 58
        return $result;
726
    }
727
728
    /**
729
     * Execute a query that fetches data.
730
     *
731
     * @param string $sql The query to execute.
732
     * @param array $params Input parameters for the query.
733
     * @param array $options Additional options.
734
     * @return \PDOStatement Returns the result of the query.
735
     * @throws \PDOException Throws an exception if something went wrong during the query.
736
     */
737 85
    protected function query($sql, array $params = [], array $options = []) {
738
        $options += [
739 85
            Db::OPTION_FETCH_MODE => $this->getFetchArgs()
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
740
        ];
741
742 85
        $stm = $this->getPDO()->prepare($sql);
743
744
745 85
        if ($options[Db::OPTION_FETCH_MODE]) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
746 78
            $stm->setFetchMode(...(array)$options[Db::OPTION_FETCH_MODE]);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
747
        }
748
749 85
        $r = $stm->execute($params);
750
751
        // This is a kludge for those that don't have errors turning into exceptions.
752 85
        if ($r === false) {
753
            list($state, $code, $msg) = $stm->errorInfo();
0 ignored issues
show
Unused Code introduced by
The assignment to $state is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
754
            throw new \PDOException($msg, $code);
755
        }
756
757 85
        return $stm;
758
    }
759
760
    /**
761
     * Query the database and return a row count.
762
     *
763
     * @param string $sql The query to execute.
764
     * @param array $params Input parameters for the query.
765
     * @param array $options Additional options.
766
     * @return int
767
     */
768 38
    protected function queryModify($sql, array $params = [], array $options = []) {
769 38
        $options += [Db::OPTION_FETCH_MODE => 0];
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
770 38
        $stm = $this->query($sql, $params, $options);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
771 38
        return $stm->rowCount();
772
    }
773
774
    /**
775
     * Query the database and return the ID of the record that was inserted.
776
     *
777
     * @param string $sql The query to execute.
778
     * @param array $params Input parameters for the query.
779
     * @param array $options Additional options.
780
     * @return int Returns the record ID.
781
     */
782 17
    protected function queryID($sql, array $params = [], array $options = []) {
783 17
        $options += [Db::OPTION_FETCH_MODE => 0];
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
784 17
        $stm = $this->query($sql, $params, $options);
0 ignored issues
show
Unused Code introduced by
$stm is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
785 17
        $r = $this->getPDO()->lastInsertId();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 8 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
786 17
        return (int)$r;
787
    }
788
789
    /**
790
     * Query the database for a database define.
791
     *
792
     * @param string $sql The query to execute.
793
     * @param array $options Additional options.
794
     */
795 28
    protected function queryDefine($sql, array $options = []) {
796 28
        $options += [Db::OPTION_FETCH_MODE => 0];
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
797 28
        $stm = $this->query($sql, [], $options);
0 ignored issues
show
Unused Code introduced by
$stm is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
798 28
    }
799
800
    /**
801
     * Safely get a value out of an array.
802
     *
803
     * This function will always return a value even if the array key doesn't exist.
804
     * The self::val() function is one of the biggest workhorses of Vanilla and shows up a lot throughout other code.
805
     * It's much preferable to use this function if your not sure whether or not an array key exists rather than
806
     * using @ error suppression.
807
     *
808
     * This function uses optimizations found in the [facebook libphputil library](https://github.com/facebook/libphutil).
809
     *
810
     * @param string|int $key The array key.
811
     * @param array|object $array The array to get the value from.
812
     * @param mixed $default The default value to return if the key doesn't exist.
813
     * @return mixed The item from the array or `$default` if the array key doesn't exist.
814
     * @category Array Functions
815
     */
816 82
    protected static function val($key, $array, $default = null) {
817 82
        if (is_array($array)) {
818
            // isset() is a micro-optimization - it is fast but fails for null values.
819 82
            if (isset($array[$key])) {
820 75
                return $array[$key];
821
            }
822
823
            // Comparing $default is also a micro-optimization.
824 82
            if ($default === null || array_key_exists($key, $array)) {
825 82
                return null;
826
            }
827
        } elseif (is_object($array)) {
828
            if (isset($array->$key)) {
829
                return $array->$key;
830
            }
831
832
            if ($default === null || property_exists($array, $key)) {
833
                return null;
834
            }
835
        }
836
837 4
        return $default;
838
    }
839
840
    /**
841
     * Escape an identifier.
842
     *
843
     * @param string|Literal $identifier The identifier to escape.
844
     * @return string Returns the field properly escaped.
845
     */
846 89
    public function escape($identifier) {
847 89
        if ($identifier instanceof Literal) {
848
            return $identifier->getValue($this);
849
        }
850 89
        return '`'.str_replace('`', '``', $identifier).'`';
851
    }
852
853
    /**
854
     * Escape a a like string so that none of its characters work as wildcards.
855
     *
856
     * @param string $str The string to escape.
857
     * @return string Returns an escaped string.
858
     */
859
    protected function escapeLike($str) {
860
        return addcslashes($str, '_%');
861
    }
862
863
    /**
864
     * Prefix a table name.
865
     *
866
     * @param string|Identifier $table The name of the table to prefix.
867
     * @param bool $escape Whether or not to escape the output.
868
     * @return string Returns a full table name.
869
     */
870 89
    protected function prefixTable($table, $escape = true) {
871 89
        if ($table instanceof Identifier) {
872 5
            return $escape ? $table->escape($this) : (string)$table;
873
        } else {
874 89
            $table = $this->px.$table;
875 89
            return $escape ? $this->escape($table) : $table;
876
        }
877
    }
878
879
    /**
880
     * Strip the database prefix off a table name.
881
     *
882
     * @param string $table The name of the table to strip.
883
     * @return string Returns the table name stripped of the prefix.
884
     */
885
    protected function stripPrefix($table) {
886
        $len = strlen($this->px);
887
        if (strcasecmp(substr($table, 0, $len), $this->px) === 0) {
888
            $table = substr($table, $len);
889
        }
890
        return $table;
891
    }
892
893
    /**
894
     * Optionally quote a where value.
895
     *
896
     * @param mixed $value The value to quote.
897
     * @param string $column The column being operated on. It must already be quoted.
898
     * @return string Returns the value, optionally quoted.
899
     * @internal param bool $quote Whether or not to quote the value.
900
     */
901 41
    public function quote($value, $column = '') {
902 41
        if ($value instanceof Literal) {
903
            /* @var Literal $value */
904 25
            return $value->getValue($this, $column);
905
        } else {
906 36
            return $this->getPDO()->quote($value);
907
        }
908
    }
909
910
    /**
911
     * Gets the {@link PDO} object for this connection.
912
     *
913
     * @return \PDO
914
     */
915 89
    public function getPDO() {
916 89
        return $this->pdo;
917
    }
918
919
    /**
920
     * Set the connection to the database.
921
     *
922
     * @param PDO $pdo The new connection to the database.
923
     * @return $this
924
     */
925
    public function setPDO(PDO $pdo) {
926
        $this->pdo = $pdo;
927
        return $this;
928
    }
929
}
930