This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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 | * A {@link Db} class for connecting to SQLite. |
||
14 | */ |
||
15 | class SqliteDb extends MySqlDb { |
||
16 | /** |
||
17 | * {@inheritdoc} |
||
18 | */ |
||
19 | 4 | protected function alterTable($tablename, array $alterdef, array $options = []) { |
|
20 | 4 | $this->alterTableMigrate($tablename, $alterdef, $options); |
|
21 | 4 | } |
|
22 | |||
23 | /** |
||
24 | * Alter a table by creating a new table and copying the old table's data to it. |
||
25 | * |
||
26 | * @param string $tablename The table to alter. |
||
27 | * @param array $alterDef The new definition. |
||
28 | * @param array $options An array of options for the migration. |
||
29 | */ |
||
30 | 4 | protected function alterTableMigrate($tablename, array $alterDef, array $options = []) { |
|
31 | 4 | $currentDef = $this->getTableDef($tablename); |
|
32 | |||
33 | // Merge the table definitions if we aren't dropping stuff. |
||
34 | 4 | if (!val(Db::OPTION_DROP, $options)) { |
|
35 | 3 | $tableDef = $this->mergeTableDefs($currentDef, $alterDef); |
|
36 | 3 | } else { |
|
37 | 1 | $tableDef = $alterDef['def']; |
|
38 | } |
||
39 | |||
40 | // Drop all of the indexes on the current table. |
||
41 | 4 | foreach (val('indexes', $currentDef, []) as $indexDef) { |
|
42 | 4 | if (val('type', $indexDef, Db::INDEX_IX) === Db::INDEX_IX) { |
|
43 | 2 | $this->dropIndex($indexDef['name']); |
|
44 | 2 | } |
|
45 | 4 | } |
|
46 | |||
47 | 4 | $tmpTablename = $tablename.'_'.time(); |
|
48 | |||
49 | // Rename the current table. |
||
50 | 4 | $this->renameTable($tablename, $tmpTablename); |
|
51 | |||
52 | // Create the new table. |
||
53 | 4 | $this->createTable($tablename, $tableDef, $options); |
|
54 | |||
55 | // Figure out the columns that we can insert. |
||
56 | 4 | $columns = array_keys(array_intersect_key($tableDef['columns'], $currentDef['columns'])); |
|
57 | |||
58 | // Build the insert/select statement. |
||
59 | 4 | $sql = 'insert into '.$this->backtick($this->px.$tablename)."\n". |
|
60 | 4 | $this->bracketList($columns, '`')."\n". |
|
61 | 4 | $this->buildSelect($tmpTablename, [], ['columns' => $columns]); |
|
62 | |||
63 | 4 | $this->query($sql, Db::QUERY_WRITE); |
|
64 | |||
65 | // Drop the temp table. |
||
66 | 4 | $this->dropTable($tmpTablename); |
|
67 | 4 | } |
|
68 | |||
69 | /** |
||
70 | * Rename a table. |
||
71 | * |
||
72 | * @param string $oldname The old name of the table. |
||
73 | * @param string $newname The new name of the table. |
||
74 | */ |
||
75 | 4 | protected function renameTable($oldname, $newname) { |
|
76 | $renameSql = 'alter table '. |
||
77 | 4 | $this->backtick($this->px.$oldname). |
|
78 | 4 | ' rename to '. |
|
79 | 4 | $this->backtick($this->px.$newname); |
|
80 | 4 | $this->query($renameSql, Db::QUERY_WRITE); |
|
81 | 4 | } |
|
82 | |||
83 | /** |
||
84 | * Merge a table def with its alter def so that no columns/indexes are lost in an alter. |
||
85 | * |
||
86 | * @param array $tableDef The table def. |
||
87 | * @param array $alterDef The alter def. |
||
88 | * @return array The new table def. |
||
89 | */ |
||
90 | 3 | protected function mergeTableDefs(array $tableDef, array $alterDef) { |
|
91 | 3 | $result = $tableDef; |
|
92 | |||
93 | 3 | $result['columns'] = array_merge($result['columns'], $alterDef['def']['columns']); |
|
94 | 3 | $result['indexes'] = array_merge($result['indexes'], $alterDef['add']['indexes']); |
|
95 | |||
96 | 3 | return $result; |
|
97 | } |
||
98 | |||
99 | /** |
||
100 | * Drop an index. |
||
101 | * |
||
102 | * @param string $indexName The name of the index to drop. |
||
103 | */ |
||
104 | 2 | protected function dropIndex($indexName) { |
|
105 | $sql = 'drop index if exists '. |
||
106 | 2 | $this->backtick($indexName); |
|
107 | 2 | $this->query($sql, Db::QUERY_DEFINE); |
|
108 | 2 | } |
|
109 | |||
110 | /** |
||
111 | * {@inheritdoc} |
||
112 | */ |
||
113 | 24 | protected function buildInsert($tablename, array $row, $quotevals = true, $options = []) { |
|
114 | 24 | if (val(Db::OPTION_UPSERT, $options)) { |
|
115 | throw new \Exception("Upsert is not supported."); |
||
116 | 24 | } elseif (val(Db::OPTION_IGNORE, $options)) { |
|
117 | 2 | $sql = 'insert or ignore into '; |
|
118 | 24 | } elseif (val(Db::OPTION_REPLACE, $options)) { |
|
119 | 2 | $sql = 'insert or replace into '; |
|
120 | 2 | } else { |
|
121 | 22 | $sql = 'insert into '; |
|
122 | } |
||
123 | 24 | $sql .= $this->backtick($this->px.$tablename); |
|
124 | |||
125 | // Add the list of values. |
||
126 | $sql .= |
||
127 | 24 | "\n".$this->bracketList(array_keys($row), '`'). |
|
128 | 24 | "\nvalues".$this->bracketList($row, $quotevals ? "'" : ''); |
|
129 | |||
130 | 24 | return $sql; |
|
131 | } |
||
132 | |||
133 | /** |
||
134 | * {@inheritdoc} |
||
135 | */ |
||
136 | 1 | protected function buildLike($column, $value, $quotevals) { |
|
137 | 1 | return "$column like ".$this->quoteVal($value, $quotevals)." escape '\\'"; |
|
138 | } |
||
139 | |||
140 | /** |
||
141 | * {@inheritdoc} |
||
142 | */ |
||
143 | 4 | View Code Duplication | protected function buildUpdate($tablename, array $set, array $where, $quotevals = true, array $options = []) { |
144 | $sql = 'update '. |
||
145 | 4 | (val(Db::OPTION_IGNORE, $options) ? 'or ignore ' : ''). |
|
146 | 4 | $this->backtick($this->px.$tablename). |
|
147 | 4 | "\nset\n "; |
|
148 | |||
149 | 4 | $parts = []; |
|
150 | 4 | foreach ($set as $key => $value) { |
|
151 | 4 | $parts[] = $this->backtick($key).' = '.$this->quoteVal($value, $quotevals); |
|
152 | 4 | } |
|
153 | 4 | $sql .= implode(",\n ", $parts); |
|
154 | |||
155 | 4 | if (!empty($where)) { |
|
156 | 4 | $sql .= "\nwhere ".$this->buildWhere($where, Db::OP_AND, $quotevals); |
|
157 | 4 | } |
|
158 | |||
159 | 4 | return $sql; |
|
160 | } |
||
161 | |||
162 | /** |
||
163 | * Construct a column definition string from an array defintion. |
||
164 | * |
||
165 | * @param string $name The name of the column. |
||
166 | * @param array $def The column definition. |
||
167 | * @return string Returns a string representing the column definition. |
||
168 | */ |
||
169 | 8 | protected function columnDefString($name, array $def) { |
|
170 | // Auto-increments MUST be of type integer. |
||
171 | 8 | if (val('autoincrement', $def)) { |
|
172 | 2 | $def['type'] = 'integer'; |
|
173 | 2 | } |
|
174 | |||
175 | 8 | $result = $this->backtick($name).' '.$this->columnTypeString($def['type']); |
|
176 | |||
177 | 8 | if (val('primary', $def) && val('autoincrement', $def)) { |
|
178 | // if (val('autoincrement', $def)) { |
||
179 | 2 | $result .= ' primary key autoincrement'; |
|
180 | 2 | $def['primary'] = true; |
|
181 | // } |
||
182 | 8 | } elseif (isset($def['default'])) { |
|
183 | 5 | $result .= ' default '.$this->quoteVal($def['default']); |
|
184 | 8 | } elseif (val('required', $def)) { |
|
185 | 7 | $result .= ' not null'; |
|
186 | 7 | } |
|
187 | |||
188 | 8 | return $result; |
|
189 | } |
||
190 | |||
191 | /** |
||
192 | * {@inheritdoc} |
||
193 | */ |
||
194 | 8 | protected function createTable($tablename, array $tabledef, array $options = []) { |
|
195 | 8 | $parts = []; |
|
196 | |||
197 | // Make sure the primary key columns are defined first and in order. |
||
198 | 8 | $autoinc = false; |
|
199 | 8 | if (isset($tabledef['indexes']['primary'])) { |
|
200 | 5 | $pkIndex = $tabledef['indexes']['primary']; |
|
201 | 5 | foreach ($pkIndex['columns'] as $column) { |
|
202 | 5 | $cdef = $tabledef['columns'][$column]; |
|
203 | 5 | $parts[] = $this->columnDefString($column, $cdef); |
|
204 | 5 | $autoinc |= val('autoincrement', $cdef, false); |
|
205 | 5 | unset($tabledef['columns'][$column]); |
|
206 | 5 | } |
|
207 | 5 | } |
|
208 | |||
209 | 8 | foreach ($tabledef['columns'] as $name => $cdef) { |
|
210 | 7 | $parts[] = $this->columnDefString($name, $cdef); |
|
211 | 8 | } |
|
212 | |||
213 | // Add the prinary key index. |
||
214 | 8 | if (isset($pkIndex) && !$autoinc) { |
|
215 | 3 | $parts[] = 'primary key '.$this->bracketList($pkIndex['columns'], '`'); |
|
216 | 3 | } |
|
217 | |||
218 | 8 | $fullTablename = $this->backtick($this->px.$tablename); |
|
219 | 8 | $sql = "create table $fullTablename (\n ". |
|
220 | 8 | implode(",\n ", $parts). |
|
221 | 8 | "\n)"; |
|
222 | |||
223 | 8 | $this->query($sql, Db::QUERY_DEFINE); |
|
224 | |||
225 | // Add the rest of the indexes. |
||
226 | 8 | foreach (val('indexes', $tabledef, []) as $index) { |
|
227 | 8 | if (val('type', $index, Db::INDEX_IX) !== Db::INDEX_PK) { |
|
228 | 5 | $this->createIndex($tablename, $index, $options); |
|
229 | 5 | } |
|
230 | 8 | } |
|
231 | 8 | } |
|
232 | |||
233 | /** |
||
234 | * Create an index. |
||
235 | * |
||
236 | * @param string $tablename The name of the table to create the index on. |
||
237 | * @param array $indexDef The index definition. |
||
238 | * @param array $options Additional options for the index creation. |
||
239 | */ |
||
240 | 5 | public function createIndex($tablename, array $indexDef, $options = []) { |
|
241 | $sql = 'create '. |
||
242 | 5 | (val('type', $indexDef) === Db::INDEX_UNIQUE ? 'unique ' : ''). |
|
243 | 5 | 'index '. |
|
244 | 5 | (val(Db::OPTION_IGNORE, $options) ? 'if not exists ' : ''). |
|
245 | 5 | $this->buildIndexName($tablename, $indexDef). |
|
246 | 5 | ' on '. |
|
247 | 5 | $this->backtick($this->px.$tablename). |
|
248 | 5 | $this->bracketList($indexDef['columns'], '`'); |
|
249 | |||
250 | 5 | $this->query($sql, Db::QUERY_DEFINE); |
|
251 | 5 | } |
|
252 | |||
253 | /** |
||
254 | * Force a value into the appropriate php type based on its Sqlite type. |
||
255 | * |
||
256 | * @param mixed $value The value to force. |
||
257 | * @param string $type The sqlite type name. |
||
258 | * @return mixed Returns $value cast to the appropriate type. |
||
259 | */ |
||
260 | View Code Duplication | protected function forceType($value, $type) { |
|
261 | $type = strtolower($type); |
||
262 | |||
263 | if ($type === 'null') { |
||
264 | return null; |
||
265 | } elseif (in_array($type, ['int', 'integer', 'tinyint', 'smallint', |
||
266 | 'mediumint', 'bigint', 'unsigned big int', 'int2', 'int8', 'boolean'])) { |
||
267 | return force_int($value); |
||
268 | } elseif (in_array($type, ['real', 'double', 'double precision', 'float', |
||
269 | 'numeric', 'decimal(10,5)'])) { |
||
270 | return floatval($value); |
||
271 | } else { |
||
272 | return (string)$value; |
||
273 | } |
||
274 | } |
||
275 | |||
276 | /** |
||
277 | * Get the columns for tables and put them in {MySqlDb::$tables}. |
||
278 | * |
||
279 | * @param string $tablename The table to get the columns for or blank for all columns. |
||
280 | * @return array|null Returns an array of columns if {@link $tablename} is specified, or null otherwise. |
||
281 | */ |
||
282 | 5 | protected function getColumns($tablename = '') { |
|
283 | 5 | if (!$tablename) { |
|
284 | 1 | $tablenames = $this->getTablenames(); |
|
285 | 1 | foreach ($tablenames as $tablename) { |
|
286 | 1 | $this->getColumns($tablename); |
|
287 | 1 | } |
|
288 | 1 | } |
|
289 | |||
290 | 5 | $cdefs = (array)$this->query('pragma table_info('.$this->quoteVal($this->px.$tablename).')'); |
|
291 | 5 | if (empty($cdefs)) { |
|
292 | 3 | return null; |
|
293 | } |
||
294 | |||
295 | 5 | $columns = []; |
|
296 | 5 | $pk = []; |
|
297 | 5 | foreach ($cdefs as $cdef) { |
|
298 | $column = [ |
||
299 | 5 | 'type' => $this->columnTypeString($cdef['type']), |
|
300 | 5 | 'required' => force_bool($cdef['notnull']), |
|
301 | 5 | ]; |
|
302 | 5 | if ($cdef['pk']) { |
|
303 | 3 | $pk[] = $cdef['name']; |
|
304 | 3 | if (strcasecmp($cdef['type'], 'integer') === 0) { |
|
305 | 1 | $column['autoincrement'] = true; |
|
306 | 1 | } else { |
|
307 | 3 | $column['primary'] = true; |
|
308 | } |
||
309 | 3 | } |
|
310 | 5 | if ($cdef['dflt_value'] !== null) { |
|
311 | 3 | $column['default'] = $cdef['dflt_value']; |
|
312 | 3 | } |
|
313 | 5 | $columns[$cdef['name']] = $column; |
|
314 | 5 | } |
|
315 | 5 | $tdef = ['columns' => $columns]; |
|
316 | 5 | if (!empty($pk)) { |
|
317 | 3 | $tdef['indexes'][Db::INDEX_PK] = [ |
|
318 | 3 | 'columns' => $pk, |
|
319 | 'type' => Db::INDEX_PK |
||
320 | 3 | ]; |
|
321 | 3 | } |
|
322 | 5 | $this->tables[$tablename] = $tdef; |
|
323 | 5 | return $columns; |
|
324 | } |
||
325 | |||
326 | /** |
||
327 | * Get the indexes from the database. |
||
328 | * |
||
329 | * @param string $tablename The name of the table to get the indexes for or an empty string to get all indexes. |
||
330 | * @return array|null |
||
331 | */ |
||
332 | 5 | protected function getIndexes($tablename = '') { |
|
333 | 5 | if (!$tablename) { |
|
334 | 1 | $tablenames = $this->getTablenames(); |
|
335 | 1 | foreach ($tablenames as $tablename) { |
|
336 | 1 | $this->getIndexes($tablename); |
|
337 | 1 | } |
|
338 | 1 | } |
|
339 | |||
340 | 5 | $pk = valr(['indexes', Db::INDEX_PK], $this->tables[$tablename]); |
|
341 | |||
342 | // Reset the index list for the table. |
||
343 | 5 | $this->tables[$tablename]['indexes'] = []; |
|
344 | |||
345 | 5 | if ($pk) { |
|
346 | 3 | $this->tables[$tablename]['indexes'][Db::INDEX_PK] = $pk; |
|
347 | 3 | } |
|
348 | |||
349 | 5 | $indexInfos = (array)$this->query('pragma index_list('.$this->quoteVal($this->px.$tablename).')'); |
|
350 | 5 | foreach ($indexInfos as $row) { |
|
351 | 5 | $indexName = $row['name']; |
|
352 | 5 | if ($row['unique']) { |
|
353 | 3 | $type = Db::INDEX_UNIQUE; |
|
354 | 3 | } else { |
|
355 | 3 | $type = Db::INDEX_IX; |
|
356 | } |
||
357 | |||
358 | // Query the columns in the index. |
||
359 | 5 | $columns = (array)$this->query('pragma index_info('.$this->quoteVal($indexName).')'); |
|
360 | |||
361 | $index = [ |
||
362 | 5 | 'name' => $indexName, |
|
363 | 5 | 'columns' => array_column($columns, 'name'), |
|
364 | 'type' => $type |
||
365 | 5 | ]; |
|
366 | 5 | $this->tables[$tablename]['indexes'][] = $index; |
|
367 | 5 | } |
|
368 | |||
369 | 5 | return $this->tables[$tablename]['indexes']; |
|
370 | } |
||
371 | |||
372 | /** |
||
373 | * Get the primary or secondary keys from the given rows. |
||
374 | * |
||
375 | * @param string $tablename The name of the table. |
||
376 | * @param array $row The row to examine. |
||
377 | * @param bool $quick Whether or not to quickly look for <tablename>ID for the primary key. |
||
378 | * @return array|null Returns the primary keys and values from {@link $rows} or null if the primary key isn't found. |
||
379 | */ |
||
380 | 2 | protected function getPKValue($tablename, array $row, $quick = false) { |
|
381 | 2 | if ($quick && isset($row[$tablename.'ID'])) { |
|
382 | 1 | return [$tablename.'ID' => $row[$tablename.'ID']]; |
|
383 | } |
||
384 | |||
385 | 1 | $tdef = $this->getTableDef($tablename); |
|
386 | 1 | if (isset($tdef['indexes'][Db::INDEX_PK]['columns'])) { |
|
387 | 1 | $pkColumns = array_flip($tdef['indexes'][Db::INDEX_PK]['columns']); |
|
388 | 1 | $cols = array_intersect_key($row, $pkColumns); |
|
389 | 1 | if (count($cols) === count($pkColumns)) { |
|
390 | 1 | return $cols; |
|
391 | } |
||
392 | } |
||
393 | |||
394 | return null; |
||
395 | } |
||
396 | |||
397 | /** |
||
398 | * Get the all of tablenames in the database. |
||
399 | * |
||
400 | * @return array Returns an array of table names with prefixes stripped. |
||
401 | */ |
||
402 | 1 | View Code Duplication | protected function getTablenames() { |
403 | // Get the table names. |
||
404 | 1 | $tables = (array)$this->get( |
|
405 | 1 | 'sqlite_master', |
|
406 | [ |
||
407 | 1 | 'type' => 'table', |
|
408 | 1 | 'name' => [Db::OP_LIKE => addcslashes($this->px, '_%').'%'] |
|
409 | 1 | ], |
|
410 | [ |
||
411 | 1 | 'columns' => ['name'], |
|
412 | 'escapeTable' => false |
||
413 | 1 | ] |
|
414 | 1 | ); |
|
415 | |||
416 | // Strip the table prefixes. |
||
417 | 1 | $tables = array_map(function ($name) { |
|
418 | 1 | return ltrim_substr($name, $this->px); |
|
419 | 1 | }, array_column($tables, 'name')); |
|
420 | |||
421 | 1 | return $tables; |
|
422 | } |
||
423 | |||
424 | /** |
||
425 | * {@inheritdoc} |
||
426 | */ |
||
427 | 7 | public function insert($tablename, array $rows, array $options = []) { |
|
428 | // Sqlite doesn't support upsert so do upserts manually. |
||
429 | 7 | if (val(Db::OPTION_UPSERT, $options)) { |
|
430 | 2 | unset($options[Db::OPTION_UPSERT]); |
|
431 | |||
432 | 2 | $keys = $this->getPKValue($tablename, $rows, true); |
|
433 | 2 | if (!$keys) { |
|
434 | throw new \Exception("Cannot upsert with no key.", 500); |
||
435 | } |
||
436 | // Try updating first. |
||
437 | 2 | $updated = $this->update( |
|
438 | 2 | $tablename, |
|
439 | 2 | array_diff_key($rows, $keys), |
|
440 | 2 | $keys, |
|
441 | $options |
||
442 | 2 | ); |
|
443 | 2 | if ($updated) { |
|
444 | // Updated. |
||
445 | 2 | if (count($keys) === 1) { |
|
446 | 1 | return array_pop($keys); |
|
447 | } else { |
||
448 | 1 | return true; |
|
0 ignored issues
–
show
|
|||
449 | } |
||
450 | } |
||
451 | 1 | } |
|
452 | |||
453 | 7 | $result = parent::insert($tablename, $rows, $options); |
|
454 | 7 | return $result; |
|
455 | } |
||
456 | |||
457 | /** |
||
458 | * Gets the {@link PDO} object for this connection. |
||
459 | * |
||
460 | * @return \PDO |
||
461 | */ |
||
462 | 31 | public function pdo() { |
|
463 | 31 | $dsn = 'sqlite:'.$this->config['path']; |
|
464 | |||
465 | 31 | if (!isset($this->pdo)) { |
|
466 | $this->pdo = new PDO($dsn, val('username', $this->config, null), val('password', $this->config, null)); |
||
467 | } |
||
468 | 31 | return $this->pdo; |
|
469 | } |
||
470 | |||
471 | /** |
||
472 | * Optionally quote a where value. |
||
473 | * |
||
474 | * @param mixed $value The value to quote. |
||
475 | * @param bool $quote Whether or not to quote the value. |
||
476 | * @return string Returns the value, optionally quoted. |
||
477 | */ |
||
478 | 21 | public function quoteVal($value, $quote = true) { |
|
479 | 21 | if ($value instanceof Literal) { |
|
480 | /* @var Literal $value */ |
||
481 | return $value->getValue('mysql'); |
||
482 | 21 | } elseif (in_array(gettype($value), ['integer', 'double'])) { |
|
483 | 20 | return (string)$value; |
|
484 | 9 | } elseif ($value === true) { |
|
485 | return '1'; |
||
486 | 9 | } elseif ($value === false) { |
|
487 | return '0'; |
||
488 | 9 | } elseif ($quote) { |
|
489 | 9 | return $this->pdo()->quote($value); |
|
490 | } else { |
||
491 | return $value; |
||
492 | } |
||
493 | } |
||
494 | } |
||
495 |
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:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.