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 | namespace SilverStripe\SQLite; |
||
4 | |||
5 | use Convert; |
||
6 | use File; |
||
7 | use SilverStripe\ORM\DataList; |
||
8 | use SilverStripe\ORM\ArrayList; |
||
9 | use SilverStripe\ORM\Connect\SS_Database; |
||
10 | use Config; |
||
11 | use Deprecation; |
||
12 | use PaginatedList; |
||
13 | use SilverStripe\ORM\DataObject; |
||
14 | use SilverStripe\ORM\Queries\SQLSelect; |
||
15 | |||
16 | |||
17 | /** |
||
18 | * SQLite database controller class |
||
19 | * |
||
20 | * @package SQLite3 |
||
21 | */ |
||
22 | class SQLite3Database extends SS_Database |
||
23 | { |
||
24 | |||
25 | /** |
||
26 | * Database schema manager object |
||
27 | * |
||
28 | * @var SQLite3SchemaManager |
||
29 | */ |
||
30 | protected $schemaManager = null; |
||
31 | |||
32 | /* |
||
33 | * This holds the parameters that the original connection was created with, |
||
34 | * so we can switch back to it if necessary (used for unit tests) |
||
35 | * |
||
36 | * @var array |
||
37 | */ |
||
38 | protected $parameters; |
||
39 | |||
40 | /* |
||
41 | * if we're on a In-Memory db |
||
42 | * |
||
43 | * @var boolean |
||
44 | */ |
||
45 | protected $livesInMemory = false; |
||
46 | |||
47 | /** |
||
48 | * List of default pragma values |
||
49 | * |
||
50 | * @todo Migrate to SS config |
||
51 | * |
||
52 | * @var array |
||
53 | */ |
||
54 | public static $default_pragma = array( |
||
55 | 'encoding' => '"UTF-8"', |
||
56 | 'locking_mode' => 'NORMAL' |
||
57 | ); |
||
58 | |||
59 | |||
60 | /** |
||
61 | * Extension used to distinguish between sqllite database files and other files. |
||
62 | * Required to handle multiple databases. |
||
63 | * |
||
64 | * @return string |
||
65 | */ |
||
66 | public static function database_extension() |
||
67 | { |
||
68 | return Config::inst()->get('SilverStripe\\SQLite\\SQLite3Database', 'database_extension'); |
||
69 | } |
||
70 | |||
71 | /** |
||
72 | * Check if a database name has a valid extension |
||
73 | * |
||
74 | * @param string $name |
||
75 | * @return boolean |
||
76 | */ |
||
77 | public static function is_valid_database_name($name) |
||
78 | { |
||
79 | $extension = self::database_extension(); |
||
80 | if (empty($extension)) { |
||
81 | return true; |
||
82 | } |
||
83 | |||
84 | return substr_compare($name, $extension, -strlen($extension), strlen($extension)) === 0; |
||
85 | } |
||
86 | |||
87 | /** |
||
88 | * Connect to a SQLite3 database. |
||
89 | * @param array $parameters An map of parameters, which should include: |
||
90 | * - database: The database to connect to, with the correct file extension (.sqlite) |
||
91 | * - path: the path to the SQLite3 database file |
||
92 | * - key: the encryption key (needs testing) |
||
93 | * - memory: use the faster In-Memory database for unit tests |
||
94 | */ |
||
95 | public function connect($parameters) |
||
96 | { |
||
97 | if (!empty($parameters['memory'])) { |
||
98 | Deprecation::notice( |
||
99 | '1.4.0', |
||
100 | "\$databaseConfig['memory'] is deprecated. Use \$databaseConfig['path'] = ':memory:' instead.", |
||
101 | Deprecation::SCOPE_GLOBAL |
||
102 | ); |
||
103 | unset($parameters['memory']); |
||
104 | $parameters['path'] = ':memory:'; |
||
105 | } |
||
106 | |||
107 | //We will store these connection parameters for use elsewhere (ie, unit tests) |
||
108 | $this->parameters = $parameters; |
||
109 | $this->schemaManager->flushCache(); |
||
110 | |||
111 | // Ensure database name is set |
||
112 | if (empty($parameters['database'])) { |
||
113 | $parameters['database'] = 'database' . self::database_extension(); |
||
114 | } |
||
115 | $dbName = $parameters['database']; |
||
116 | if (!self::is_valid_database_name($dbName)) { |
||
117 | // If not using the correct file extension for database files then the |
||
118 | // results of SQLite3SchemaManager::databaseList will be unpredictable |
||
119 | $extension = self::database_extension(); |
||
120 | Deprecation::notice('3.2', "SQLite3Database now expects a database file with extension \"$extension\". Behaviour may be unpredictable otherwise."); |
||
121 | } |
||
122 | |||
123 | // use the very lightspeed SQLite In-Memory feature for testing |
||
124 | if ($this->getLivesInMemory()) { |
||
125 | $file = ':memory:'; |
||
126 | } else { |
||
127 | // Ensure path is given |
||
128 | if (empty($parameters['path'])) { |
||
129 | $parameters['path'] = ASSETS_PATH . '/.sqlitedb'; |
||
130 | } |
||
131 | |||
132 | //assumes that the path to dbname will always be provided: |
||
133 | $file = $parameters['path'] . '/' . $dbName; |
||
134 | if (!file_exists($parameters['path'])) { |
||
135 | SQLiteDatabaseConfigurationHelper::create_db_dir($parameters['path']); |
||
136 | SQLiteDatabaseConfigurationHelper::secure_db_dir($parameters['path']); |
||
137 | } |
||
138 | } |
||
139 | |||
140 | // 'path' and 'database' are merged into the full file path, which |
||
141 | // is the format that connectors such as PDOConnector expect |
||
142 | $parameters['filepath'] = $file; |
||
143 | |||
144 | // Ensure that driver is available (required by PDO) |
||
145 | if (empty($parameters['driver'])) { |
||
146 | $parameters['driver'] = $this->getDatabaseServer(); |
||
147 | } |
||
148 | |||
149 | $this->connector->connect($parameters, true); |
||
150 | |||
151 | foreach (self::$default_pragma as $pragma => $value) { |
||
152 | $this->setPragma($pragma, $value); |
||
153 | } |
||
154 | |||
155 | if (empty(self::$default_pragma['locking_mode'])) { |
||
156 | self::$default_pragma['locking_mode'] = $this->getPragma('locking_mode'); |
||
157 | } |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * Retrieve parameters used to connect to this SQLLite database |
||
162 | * |
||
163 | * @return array |
||
164 | */ |
||
165 | public function getParameters() |
||
166 | { |
||
167 | return $this->parameters; |
||
168 | } |
||
169 | |||
170 | public function getLivesInMemory() |
||
171 | { |
||
172 | return isset($this->parameters['path']) && $this->parameters['path'] === ':memory:'; |
||
173 | } |
||
174 | |||
175 | public function supportsCollations() |
||
176 | { |
||
177 | return true; |
||
178 | } |
||
179 | |||
180 | public function supportsTimezoneOverride() |
||
181 | { |
||
182 | return false; |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * Execute PRAGMA commands. |
||
187 | * |
||
188 | * @param string $pragma name |
||
189 | * @param string $value to set |
||
190 | */ |
||
191 | public function setPragma($pragma, $value) |
||
192 | { |
||
193 | $this->query("PRAGMA $pragma = $value"); |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * Gets pragma value. |
||
198 | * |
||
199 | * @param string $pragma name |
||
200 | * @return string the pragma value |
||
201 | */ |
||
202 | public function getPragma($pragma) |
||
203 | { |
||
204 | return $this->query("PRAGMA $pragma")->value(); |
||
205 | } |
||
206 | |||
207 | public function getDatabaseServer() |
||
208 | { |
||
209 | return "sqlite"; |
||
210 | } |
||
211 | |||
212 | public function selectDatabase($name, $create = false, $errorLevel = E_USER_ERROR) |
||
213 | { |
||
214 | if (!$this->schemaManager->databaseExists($name)) { |
||
215 | // Check DB creation permisson |
||
216 | if (!$create) { |
||
217 | if ($errorLevel !== false) { |
||
218 | user_error("Attempted to connect to non-existing database \"$name\"", $errorLevel); |
||
219 | } |
||
220 | // Unselect database |
||
221 | $this->connector->unloadDatabase(); |
||
222 | return false; |
||
223 | } |
||
224 | $this->schemaManager->createDatabase($name); |
||
225 | } |
||
226 | |||
227 | // Reconnect using the existing parameters |
||
228 | $parameters = $this->parameters; |
||
229 | $parameters['database'] = $name; |
||
230 | $this->connect($parameters); |
||
231 | return true; |
||
232 | } |
||
233 | |||
234 | public function now() |
||
235 | { |
||
236 | return "datetime('now', 'localtime')"; |
||
237 | } |
||
238 | |||
239 | public function random() |
||
240 | { |
||
241 | return 'random()'; |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * The core search engine configuration. |
||
246 | * @todo There is a fulltext search for SQLite making use of virtual tables, the fts3 extension and the |
||
247 | * MATCH operator |
||
248 | * there are a few issues with fts: |
||
249 | * - shared cached lock doesn't allow to create virtual tables on versions prior to 3.6.17 |
||
250 | * - there must not be more than one MATCH operator per statement |
||
251 | * - the fts3 extension needs to be available |
||
252 | * for now we use the MySQL implementation with the MATCH()AGAINST() uglily replaced with LIKE |
||
253 | * |
||
254 | * @param array $classesToSearch |
||
255 | * @param string $keywords Keywords as a space separated string |
||
256 | * @param int $start |
||
257 | * @param int $pageLength |
||
258 | * @param string $sortBy |
||
259 | * @param string $extraFilter |
||
260 | * @param bool $booleanSearch |
||
261 | * @param string $alternativeFileFilter |
||
262 | * @param bool $invertedMatch |
||
263 | * @return PaginatedList DataObjectSet of result pages |
||
264 | */ |
||
265 | public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC", |
||
266 | $extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false |
||
267 | ) { |
||
268 | $keywords = $this->escapeString(str_replace(array('*', '+', '-', '"', '\''), '', $keywords)); |
||
269 | $htmlEntityKeywords = htmlentities(utf8_decode($keywords)); |
||
270 | |||
271 | $pageClass = 'SilverStripe\\CMS\\Model\\SiteTree'; |
||
272 | $fileClass = 'File'; |
||
273 | |||
274 | $extraFilters = array($pageClass => '', $fileClass => ''); |
||
275 | |||
276 | if ($extraFilter) { |
||
277 | $extraFilters[$pageClass] = " AND $extraFilter"; |
||
278 | |||
279 | if ($alternativeFileFilter) { |
||
280 | $extraFilters[$fileClass] = " AND $alternativeFileFilter"; |
||
281 | } else { |
||
282 | $extraFilters[$fileClass] = $extraFilters[$pageClass]; |
||
283 | } |
||
284 | } |
||
285 | |||
286 | // Always ensure that only pages with ShowInSearch = 1 can be searched |
||
287 | $extraFilters[$pageClass] .= ' AND ShowInSearch <> 0'; |
||
288 | // File.ShowInSearch was added later, keep the database driver backwards compatible |
||
289 | // by checking for its existence first |
||
290 | if (File::singleton()->db('ShowInSearch')) { |
||
291 | $extraFilters[$fileClass] .= " AND ShowInSearch <> 0"; |
||
292 | } |
||
293 | |||
294 | $limit = $start . ", " . (int) $pageLength; |
||
295 | |||
296 | $notMatch = $invertedMatch ? "NOT " : ""; |
||
297 | if ($keywords) { |
||
298 | $match[$pageClass] = " |
||
0 ignored issues
–
show
|
|||
299 | (Title LIKE '%$keywords%' OR MenuTitle LIKE '%$keywords%' OR Content LIKE '%$keywords%' OR MetaDescription LIKE '%$keywords%' OR |
||
300 | Title LIKE '%$htmlEntityKeywords%' OR MenuTitle LIKE '%$htmlEntityKeywords%' OR Content LIKE '%$htmlEntityKeywords%' OR MetaDescription LIKE '%$htmlEntityKeywords%') |
||
301 | "; |
||
302 | $fileClassSQL = Convert::raw2sql($fileClass); |
||
303 | $match[$fileClass] = "(Name LIKE '%$keywords%' OR Title LIKE '%$keywords%') AND ClassName = '$fileClassSQL'"; |
||
304 | |||
305 | // We make the relevance search by converting a boolean mode search into a normal one |
||
306 | $relevanceKeywords = $keywords; |
||
307 | $htmlEntityRelevanceKeywords = $htmlEntityKeywords; |
||
308 | $relevance[$pageClass] = "(Title LIKE '%$relevanceKeywords%' OR MenuTitle LIKE '%$relevanceKeywords%' OR Content LIKE '%$relevanceKeywords%' OR MetaDescription LIKE '%$relevanceKeywords%') + (Title LIKE '%$htmlEntityRelevanceKeywords%' OR MenuTitle LIKE '%$htmlEntityRelevanceKeywords%' OR Content LIKE '%$htmlEntityRelevanceKeywords%' OR MetaDescription LIKE '%$htmlEntityRelevanceKeywords%')"; |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$relevance was never initialized. Although not strictly required by PHP, it is generally a good practice to add $relevance = array(); before regardless.
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code. Let’s take a look at an example: foreach ($collection as $item) {
$myArray['foo'] = $item->getFoo();
if ($item->hasBar()) {
$myArray['bar'] = $item->getBar();
}
// do something with $myArray
}
As you can see in this example, the array This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop. ![]() |
|||
309 | $relevance[$fileClass] = "(Name LIKE '%$relevanceKeywords%' OR Title LIKE '%$relevanceKeywords%')"; |
||
310 | } else { |
||
311 | $relevance[$pageClass] = $relevance[$fileClass] = 1; |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$relevance was never initialized. Although not strictly required by PHP, it is generally a good practice to add $relevance = array(); before regardless.
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code. Let’s take a look at an example: foreach ($collection as $item) {
$myArray['foo'] = $item->getFoo();
if ($item->hasBar()) {
$myArray['bar'] = $item->getBar();
}
// do something with $myArray
}
As you can see in this example, the array This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop. ![]() |
|||
312 | $match[$pageClass] = $match[$fileClass] = "1 = 1"; |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$match was never initialized. Although not strictly required by PHP, it is generally a good practice to add $match = array(); before regardless.
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code. Let’s take a look at an example: foreach ($collection as $item) {
$myArray['foo'] = $item->getFoo();
if ($item->hasBar()) {
$myArray['bar'] = $item->getBar();
}
// do something with $myArray
}
As you can see in this example, the array This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop. ![]() |
|||
313 | } |
||
314 | |||
315 | // Generate initial queries |
||
316 | $queries = array(); |
||
317 | foreach ($classesToSearch as $class) { |
||
318 | $queries[$class] = DataList::create($class) |
||
319 | ->where($notMatch . $match[$class] . $extraFilters[$class]) |
||
320 | ->dataQuery() |
||
321 | ->query(); |
||
322 | } |
||
323 | |||
324 | // Make column selection lists |
||
325 | $select = array( |
||
326 | $pageClass => array( |
||
327 | "\"ClassName\"", |
||
328 | "\"ID\"", |
||
329 | "\"ParentID\"", |
||
330 | "\"Title\"", |
||
331 | "\"URLSegment\"", |
||
332 | "\"Content\"", |
||
333 | "\"LastEdited\"", |
||
334 | "\"Created\"", |
||
335 | "NULL AS \"Name\"", |
||
336 | "\"CanViewType\"", |
||
337 | $relevance[$pageClass] . " AS Relevance" |
||
338 | ), |
||
339 | $fileClass => array( |
||
340 | "\"ClassName\"", |
||
341 | "\"ID\"", |
||
342 | "NULL AS \"ParentID\"", |
||
343 | "\"Title\"", |
||
344 | "NULL AS \"URLSegment\"", |
||
345 | "NULL AS \"Content\"", |
||
346 | "\"LastEdited\"", |
||
347 | "\"Created\"", |
||
348 | "\"Name\"", |
||
349 | "NULL AS \"CanViewType\"", |
||
350 | $relevance[$fileClass] . " AS Relevance" |
||
351 | ) |
||
352 | ); |
||
353 | |||
354 | // Process queries |
||
355 | foreach ($classesToSearch as $class) { |
||
356 | // There's no need to do all that joining |
||
357 | $queries[$class]->setFrom('"'.DataObject::getSchema()->baseDataTable($class).'"'); |
||
358 | $queries[$class]->setSelect(array()); |
||
359 | foreach ($select[$class] as $clause) { |
||
360 | if (preg_match('/^(.*) +AS +"?([^"]*)"?/i', $clause, $matches)) { |
||
361 | $queries[$class]->selectField($matches[1], $matches[2]); |
||
362 | } else { |
||
363 | $queries[$class]->selectField(str_replace('"', '', $clause)); |
||
364 | } |
||
365 | } |
||
366 | |||
367 | $queries[$class]->setOrderBy(array()); |
||
368 | } |
||
369 | |||
370 | // Combine queries |
||
371 | $querySQLs = array(); |
||
372 | $queryParameters = array(); |
||
373 | $totalCount = 0; |
||
374 | foreach ($queries as $query) { |
||
375 | /** @var SQLSelect $query */ |
||
376 | $querySQLs[] = $query->sql($parameters); |
||
377 | $queryParameters = array_merge($queryParameters, $parameters); |
||
378 | $totalCount += $query->unlimitedRowCount(); |
||
379 | } |
||
380 | |||
381 | $fullQuery = implode(" UNION ", $querySQLs) . " ORDER BY $sortBy LIMIT $limit"; |
||
382 | // Get records |
||
383 | $records = $this->preparedQuery($fullQuery, $queryParameters); |
||
384 | |||
385 | foreach ($records as $record) { |
||
386 | $objects[] = new $record['ClassName']($record); |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$objects was never initialized. Although not strictly required by PHP, it is generally a good practice to add $objects = array(); before regardless.
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code. Let’s take a look at an example: foreach ($collection as $item) {
$myArray['foo'] = $item->getFoo();
if ($item->hasBar()) {
$myArray['bar'] = $item->getBar();
}
// do something with $myArray
}
As you can see in this example, the array This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop. ![]() |
|||
387 | } |
||
388 | |||
389 | if (isset($objects)) { |
||
390 | $doSet = new ArrayList($objects); |
||
391 | } else { |
||
392 | $doSet = new ArrayList(); |
||
393 | } |
||
394 | $list = new PaginatedList($doSet); |
||
395 | $list->setPageStart($start); |
||
396 | $list->setPageLength($pageLength); |
||
397 | $list->setTotalItems($totalCount); |
||
398 | return $list; |
||
399 | } |
||
400 | |||
401 | /* |
||
402 | * Does this database support transactions? |
||
403 | */ |
||
404 | public function supportsTransactions() |
||
405 | { |
||
406 | return version_compare($this->getVersion(), '3.6', '>='); |
||
407 | } |
||
408 | |||
409 | public function supportsExtensions($extensions = array('partitions', 'tablespaces', 'clustering')) |
||
410 | { |
||
411 | if (isset($extensions['partitions'])) { |
||
412 | return true; |
||
413 | } elseif (isset($extensions['tablespaces'])) { |
||
414 | return true; |
||
415 | } elseif (isset($extensions['clustering'])) { |
||
416 | return true; |
||
417 | } else { |
||
418 | return false; |
||
419 | } |
||
420 | } |
||
421 | |||
422 | public function transactionStart($transaction_mode = false, $session_characteristics = false) |
||
423 | { |
||
424 | $this->query('BEGIN'); |
||
425 | } |
||
426 | |||
427 | public function transactionSavepoint($savepoint) |
||
428 | { |
||
429 | $this->query("SAVEPOINT \"$savepoint\""); |
||
430 | } |
||
431 | |||
432 | public function transactionRollback($savepoint = false) |
||
433 | { |
||
434 | if ($savepoint) { |
||
435 | $this->query("ROLLBACK TO $savepoint;"); |
||
436 | } else { |
||
437 | $this->query('ROLLBACK;'); |
||
438 | } |
||
439 | } |
||
440 | |||
441 | public function transactionEnd($chain = false) |
||
442 | { |
||
443 | $this->query('COMMIT;'); |
||
444 | } |
||
445 | |||
446 | public function clearTable($table) |
||
447 | { |
||
448 | $this->query("DELETE FROM \"$table\""); |
||
449 | } |
||
450 | |||
451 | public function comparisonClause($field, $value, $exact = false, $negate = false, $caseSensitive = null, |
||
452 | $parameterised = false |
||
453 | ) { |
||
454 | if ($exact && !$caseSensitive) { |
||
0 ignored issues
–
show
The expression
$caseSensitive of type boolean|null is loosely compared to false ; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.
If an expression can have both $a = canBeFalseAndNull();
// Instead of
if ( ! $a) { }
// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
![]() |
|||
455 | $comp = ($negate) ? '!=' : '='; |
||
456 | } else { |
||
457 | if ($caseSensitive) { |
||
458 | // GLOB uses asterisks as wildcards. |
||
459 | // Replace them in search string, without replacing escaped percetage signs. |
||
460 | $comp = 'GLOB'; |
||
461 | $value = preg_replace('/^%([^\\\\])/', '*$1', $value); |
||
462 | $value = preg_replace('/([^\\\\])%$/', '$1*', $value); |
||
463 | $value = preg_replace('/([^\\\\])%/', '$1*', $value); |
||
464 | } else { |
||
465 | $comp = 'LIKE'; |
||
466 | } |
||
467 | if ($negate) { |
||
468 | $comp = 'NOT ' . $comp; |
||
469 | } |
||
470 | } |
||
471 | |||
472 | if ($parameterised) { |
||
473 | return sprintf("%s %s ?", $field, $comp); |
||
474 | } else { |
||
475 | return sprintf("%s %s '%s'", $field, $comp, $value); |
||
476 | } |
||
477 | } |
||
478 | |||
479 | public function formattedDatetimeClause($date, $format) |
||
480 | { |
||
481 | preg_match_all('/%(.)/', $format, $matches); |
||
482 | foreach ($matches[1] as $match) { |
||
483 | if (array_search($match, array('Y', 'm', 'd', 'H', 'i', 's', 'U')) === false) { |
||
484 | user_error('formattedDatetimeClause(): unsupported format character %' . $match, E_USER_WARNING); |
||
485 | } |
||
486 | } |
||
487 | |||
488 | $translate = array( |
||
489 | '/%i/' => '%M', |
||
490 | '/%s/' => '%S', |
||
491 | '/%U/' => '%s', |
||
492 | ); |
||
493 | $format = preg_replace(array_keys($translate), array_values($translate), $format); |
||
494 | |||
495 | $modifiers = array(); |
||
496 | if ($format == '%s' && $date != 'now') { |
||
497 | $modifiers[] = 'utc'; |
||
498 | } |
||
499 | if ($format != '%s' && $date == 'now') { |
||
500 | $modifiers[] = 'localtime'; |
||
501 | } |
||
502 | |||
503 | View Code Duplication | if (preg_match('/^now$/i', $date)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
504 | $date = "'now'"; |
||
505 | } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date)) { |
||
506 | $date = "'$date'"; |
||
507 | } |
||
508 | |||
509 | $modifier = empty($modifiers) ? '' : ", '" . implode("', '", $modifiers) . "'"; |
||
510 | return "strftime('$format', $date$modifier)"; |
||
511 | } |
||
512 | |||
513 | public function datetimeIntervalClause($date, $interval) |
||
514 | { |
||
515 | $modifiers = array(); |
||
516 | if ($date == 'now') { |
||
517 | $modifiers[] = 'localtime'; |
||
518 | } |
||
519 | |||
520 | View Code Duplication | if (preg_match('/^now$/i', $date)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
521 | $date = "'now'"; |
||
522 | } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date)) { |
||
523 | $date = "'$date'"; |
||
524 | } |
||
525 | |||
526 | $modifier = empty($modifiers) ? '' : ", '" . implode("', '", $modifiers) . "'"; |
||
527 | return "datetime($date$modifier, '$interval')"; |
||
528 | } |
||
529 | |||
530 | public function datetimeDifferenceClause($date1, $date2) |
||
531 | { |
||
532 | $modifiers1 = array(); |
||
533 | $modifiers2 = array(); |
||
534 | |||
535 | if ($date1 == 'now') { |
||
536 | $modifiers1[] = 'localtime'; |
||
537 | } |
||
538 | if ($date2 == 'now') { |
||
539 | $modifiers2[] = 'localtime'; |
||
540 | } |
||
541 | |||
542 | if (preg_match('/^now$/i', $date1)) { |
||
543 | $date1 = "'now'"; |
||
544 | } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date1)) { |
||
545 | $date1 = "'$date1'"; |
||
546 | } |
||
547 | |||
548 | if (preg_match('/^now$/i', $date2)) { |
||
549 | $date2 = "'now'"; |
||
550 | } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date2)) { |
||
551 | $date2 = "'$date2'"; |
||
552 | } |
||
553 | |||
554 | $modifier1 = empty($modifiers1) ? '' : ", '" . implode("', '", $modifiers1) . "'"; |
||
555 | $modifier2 = empty($modifiers2) ? '' : ", '" . implode("', '", $modifiers2) . "'"; |
||
556 | |||
557 | return "strftime('%s', $date1$modifier1) - strftime('%s', $date2$modifier2)"; |
||
558 | } |
||
559 | } |
||
560 |
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.
Let’s take a look at an example:
As you can see in this example, the array
$myArray
is initialized the first time when the foreach loop is entered. You can also see that the value of thebar
key is only written conditionally; thus, its value might result from a previous iteration.This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.