Completed
Pull Request — master (#23)
by Damian
08:37
created

SQLite3Database::datetimeDifferenceClause()   D

Complexity

Conditions 9
Paths 144

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 29
rs 4.6666
cc 9
eloc 18
nc 144
nop 2
1
<?php
2
3
namespace SilverStripe\SQLite;
4
5
use SilverStripe\ORM\DataList;
6
use SilverStripe\ORM\ArrayList;
7
use SilverStripe\ORM\Connect\SS_Database;
8
use Config;
9
use Deprecation;
10
use PaginatedList;
11
use SilverStripe\ORM\Queries\SQLSelect;
12
13
14
/**
15
 * SQLite database controller class
16
 *
17
 * @package SQLite3
18
 */
19
class SQLite3Database extends SS_Database
20
{
21
22
    /**
23
     * Database schema manager object
24
     *
25
     * @var SQLite3SchemaManager
26
     */
27
    protected $schemaManager = null;
28
29
    /*
30
     * This holds the parameters that the original connection was created with,
31
     * so we can switch back to it if necessary (used for unit tests)
32
     *
33
     * @var array
34
     */
35
    protected $parameters;
36
37
    /*
38
     * if we're on a In-Memory db
39
     *
40
     * @var boolean
41
     */
42
    protected $livesInMemory = false;
43
44
    /**
45
     * List of default pragma values
46
     *
47
     * @todo Migrate to SS config
48
     *
49
     * @var array
50
     */
51
    public static $default_pragma = array(
52
        'encoding' => '"UTF-8"',
53
        'locking_mode' => 'NORMAL'
54
    );
55
56
57
    /**
58
     * Extension used to distinguish between sqllite database files and other files.
59
     * Required to handle multiple databases.
60
     *
61
     * @return string
62
     */
63
    public static function database_extension()
64
    {
65
        return Config::inst()->get('SQLite3Database', 'database_extension');
66
    }
67
68
    /**
69
     * Check if a database name has a valid extension
70
     *
71
     * @param string $name
72
     * @return boolean
73
     */
74
    public static function is_valid_database_name($name)
75
    {
76
        $extension = self::database_extension();
77
        if (empty($extension)) {
78
            return true;
79
        }
80
81
        return substr_compare($name, $extension, -strlen($extension), strlen($extension)) === 0;
82
    }
83
84
    /**
85
     * Connect to a SQLite3 database.
86
     * @param array $parameters An map of parameters, which should include:
87
     *  - database: The database to connect to, with the correct file extension (.sqlite)
88
     *  - path: the path to the SQLite3 database file
89
     *  - key: the encryption key (needs testing)
90
     *  - memory: use the faster In-Memory database for unit tests
91
     */
92
    public function connect($parameters)
93
    {
94
        if (!empty($parameters['memory'])) {
95
            Deprecation::notice(
96
                '1.4.0',
97
                "\$databaseConfig['memory'] is deprecated. Use \$databaseConfig['path'] = ':memory:' instead.",
98
                Deprecation::SCOPE_GLOBAL
99
            );
100
            unset($parameters['memory']);
101
            $parameters['path'] = ':memory:';
102
        }
103
104
        //We will store these connection parameters for use elsewhere (ie, unit tests)
105
        $this->parameters = $parameters;
106
        $this->schemaManager->flushCache();
107
108
        // Ensure database name is set
109
        if (empty($parameters['database'])) {
110
            $parameters['database'] = 'database' . self::database_extension();
111
        }
112
        $dbName = $parameters['database'];
113
        if (!self::is_valid_database_name($dbName)) {
114
            // If not using the correct file extension for database files then the
115
            // results of SQLite3SchemaManager::databaseList will be unpredictable
116
            $extension = self::database_extension();
117
            Deprecation::notice('3.2', "SQLite3Database now expects a database file with extension \"$extension\". Behaviour may be unpredictable otherwise.");
118
        }
119
120
        // use the very lightspeed SQLite In-Memory feature for testing
121
        if ($this->getLivesInMemory()) {
122
            $file = ':memory:';
123
        } else {
124
            // Ensure path is given
125
            if (empty($parameters['path'])) {
126
                $parameters['path'] = ASSETS_PATH . '/.sqlitedb';
127
            }
128
129
            //assumes that the path to dbname will always be provided:
130
            $file = $parameters['path'] . '/' . $dbName;
131
            if (!file_exists($parameters['path'])) {
132
                SQLiteDatabaseConfigurationHelper::create_db_dir($parameters['path']);
133
                SQLiteDatabaseConfigurationHelper::secure_db_dir($parameters['path']);
134
            }
135
        }
136
137
        // 'path' and 'database' are merged into the full file path, which
138
        // is the format that connectors such as PDOConnector expect
139
        $parameters['filepath'] = $file;
140
141
        // Ensure that driver is available (required by PDO)
142
        if (empty($parameters['driver'])) {
143
            $parameters['driver'] = $this->getDatabaseServer();
144
        }
145
146
        $this->connector->connect($parameters, true);
147
148
        foreach (self::$default_pragma as $pragma => $value) {
149
            $this->setPragma($pragma, $value);
150
        }
151
152
        if (empty(self::$default_pragma['locking_mode'])) {
153
            self::$default_pragma['locking_mode'] = $this->getPragma('locking_mode');
154
        }
155
    }
156
157
    /**
158
     * Retrieve parameters used to connect to this SQLLite database
159
     *
160
     * @return array
161
     */
162
    public function getParameters()
163
    {
164
        return $this->parameters;
165
    }
166
167
    public function getLivesInMemory()
168
    {
169
        return isset($this->parameters['path']) && $this->parameters['path'] === ':memory:';
170
    }
171
172
    public function supportsCollations()
173
    {
174
        return true;
175
    }
176
177
    public function supportsTimezoneOverride()
178
    {
179
        return false;
180
    }
181
182
    /**
183
     * Execute PRAGMA commands.
184
     *
185
     * @param string $pragma name
186
     * @param string $value to set
187
     */
188
    public function setPragma($pragma, $value)
189
    {
190
        $this->query("PRAGMA $pragma = $value");
191
    }
192
193
    /**
194
     * Gets pragma value.
195
     *
196
     * @param string $pragma name
197
     * @return string the pragma value
198
     */
199
    public function getPragma($pragma)
200
    {
201
        return $this->query("PRAGMA $pragma")->value();
202
    }
203
204
    public function getDatabaseServer()
205
    {
206
        return "sqlite";
207
    }
208
209
    public function selectDatabase($name, $create = false, $errorLevel = E_USER_ERROR)
210
    {
211
        if (!$this->schemaManager->databaseExists($name)) {
212
            // Check DB creation permisson
213
            if (!$create) {
214
                if ($errorLevel !== false) {
215
                    user_error("Attempted to connect to non-existing database \"$name\"", $errorLevel);
216
                }
217
                // Unselect database
218
                $this->connector->unloadDatabase();
219
                return false;
220
            }
221
            $this->schemaManager->createDatabase($name);
222
        }
223
224
        // Reconnect using the existing parameters
225
        $parameters = $this->parameters;
226
        $parameters['database'] = $name;
227
        $this->connect($parameters);
228
        return true;
229
    }
230
231
    public function now()
232
    {
233
        return "datetime('now', 'localtime')";
234
    }
235
236
    public function random()
237
    {
238
        return 'random()';
239
    }
240
241
    /**
242
     * The core search engine configuration.
243
     * @todo There is a fulltext search for SQLite making use of virtual tables, the fts3 extension and the
244
     * MATCH operator
245
     * there are a few issues with fts:
246
     * - shared cached lock doesn't allow to create virtual tables on versions prior to 3.6.17
247
     * - there must not be more than one MATCH operator per statement
248
     * - the fts3 extension needs to be available
249
     * for now we use the MySQL implementation with the MATCH()AGAINST() uglily replaced with LIKE
250
     *
251
     * @param array $classesToSearch
252
     * @param string $keywords Keywords as a space separated string
253
     * @param int $start
254
     * @param int $pageLength
255
     * @param string $sortBy
256
     * @param string $extraFilter
257
     * @param bool $booleanSearch
258
     * @param string $alternativeFileFilter
259
     * @param bool $invertedMatch
260
     * @return PaginatedList DataObjectSet of result pages
261
     */
262
    public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC",
263
        $extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false
264
    ) {
265
        $keywords = $this->escapeString(str_replace(array('*', '+', '-', '"', '\''), '', $keywords));
266
        $htmlEntityKeywords = htmlentities(utf8_decode($keywords));
267
268
        $extraFilters = array('SiteTree' => '', 'File' => '');
269
270
        if ($extraFilter) {
271
            $extraFilters['SiteTree'] = " AND $extraFilter";
272
273
            if ($alternativeFileFilter) {
274
                $extraFilters['File'] = " AND $alternativeFileFilter";
275
            } else {
276
                $extraFilters['File'] = $extraFilters['SiteTree'];
277
            }
278
        }
279
280
        // Always ensure that only pages with ShowInSearch = 1 can be searched
281
        $extraFilters['SiteTree'] .= ' AND ShowInSearch <> 0';
282
        // File.ShowInSearch was added later, keep the database driver backwards compatible
283
        // by checking for its existence first
284
        $fields = $this->getSchemaManager()->fieldList('File');
285
        if (array_key_exists('ShowInSearch', $fields)) {
286
            $extraFilters['File'] .= " AND ShowInSearch <> 0";
287
        }
288
289
        $limit = $start . ", " . (int) $pageLength;
290
291
        $notMatch = $invertedMatch ? "NOT " : "";
292
        if ($keywords) {
293
            $match['SiteTree'] = "
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 $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar 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.

Loading history...
294
				(Title LIKE '%$keywords%' OR MenuTitle LIKE '%$keywords%' OR Content LIKE '%$keywords%' OR MetaDescription LIKE '%$keywords%' OR
295
				Title LIKE '%$htmlEntityKeywords%' OR MenuTitle LIKE '%$htmlEntityKeywords%' OR Content LIKE '%$htmlEntityKeywords%' OR MetaDescription LIKE '%$htmlEntityKeywords%')
296
			";
297
            $match['File'] = "(Name LIKE '%$keywords%' OR Title LIKE '%$keywords%') AND ClassName = 'File'";
298
299
            // We make the relevance search by converting a boolean mode search into a normal one
300
            $relevanceKeywords = $keywords;
301
            $htmlEntityRelevanceKeywords = $htmlEntityKeywords;
302
            $relevance['SiteTree'] = "(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 $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar 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.

Loading history...
303
            $relevance['File'] = "(Name LIKE '%$relevanceKeywords%' OR Title LIKE '%$relevanceKeywords%')";
304
        } else {
305
            $relevance['SiteTree'] = $relevance['File'] = 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 $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar 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.

Loading history...
306
            $match['SiteTree'] = $match['File'] = "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 $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar 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.

Loading history...
307
        }
308
309
        // Generate initial queries and base table names
310
        $baseClasses = array('SiteTree' => '', 'File' => '');
311
        $queries = array();
312
        foreach ($classesToSearch as $class) {
313
            $queries[$class] = DataList::create($class)
314
                ->where($notMatch . $match[$class] . $extraFilters[$class])
315
                ->dataQuery()
316
                ->query();
317
            $fromArr = $queries[$class]->getFrom();
318
            $baseClasses[$class] = reset($fromArr);
319
        }
320
321
        // Make column selection lists
322
        $select = array(
323
            'SiteTree' => array(
324
                "\"ClassName\"",
325
                "\"ID\"",
326
                "\"ParentID\"",
327
                "\"Title\"",
328
                "\"URLSegment\"",
329
                "\"Content\"",
330
                "\"LastEdited\"",
331
                "\"Created\"",
332
                "NULL AS \"Name\"",
333
                "\"CanViewType\"",
334
                "$relevance[SiteTree] AS Relevance"
335
            ),
336
            'File' => array(
337
                "\"ClassName\"",
338
                "\"ID\"",
339
                "NULL AS \"ParentID\"",
340
                "\"Title\"",
341
                "NULL AS \"URLSegment\"",
342
                "NULL AS \"Content\"",
343
                "\"LastEdited\"",
344
                "\"Created\"",
345
                "\"Name\"",
346
                "NULL AS \"CanViewType\"",
347
                "$relevance[File] AS Relevance"
348
            )
349
        );
350
351
        // Process queries
352
        foreach ($classesToSearch as $class) {
353
            // There's no need to do all that joining
354
            $queries[$class]->setFrom($baseClasses[$class]);
355
356
            $queries[$class]->setSelect(array());
357
            foreach ($select[$class] as $clause) {
358
                if (preg_match('/^(.*) +AS +"?([^"]*)"?/i', $clause, $matches)) {
359
                    $queries[$class]->selectField($matches[1], $matches[2]);
360
                } else {
361
                    $queries[$class]->selectField(str_replace('"', '', $clause));
362
                }
363
            }
364
365
            $queries[$class]->setOrderBy(array());
366
        }
367
368
        // Combine queries
369
        $querySQLs = array();
370
        $queryParameters = array();
371
        $totalCount = 0;
372
        foreach ($queries as $query) {
373
            /** @var SQLSelect $query */
374
            $querySQLs[] = $query->sql($parameters);
0 ignored issues
show
Bug introduced by
The variable $parameters does not exist. Did you mean $queryParameters?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
375
            $queryParameters = array_merge($queryParameters, $parameters);
0 ignored issues
show
Bug introduced by
The variable $parameters does not exist. Did you mean $queryParameters?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
376
            $totalCount += $query->unlimitedRowCount();
377
        }
378
379
        $fullQuery = implode(" UNION ", $querySQLs) . " ORDER BY $sortBy LIMIT $limit";
380
        // Get records
381
        $records = $this->preparedQuery($fullQuery, $queryParameters);
382
383
        foreach ($records as $record) {
384
            $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 $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar 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.

Loading history...
385
        }
386
387
        if (isset($objects)) {
388
            $doSet = new ArrayList($objects);
389
        } else {
390
            $doSet = new ArrayList();
391
        }
392
        $list = new PaginatedList($doSet);
393
        $list->setPageStart($start);
394
        $list->setPageLength($pageLength);
395
        $list->setTotalItems($totalCount);
396
        return $list;
397
    }
398
399
    /*
400
     * Does this database support transactions?
401
     */
402
    public function supportsTransactions()
403
    {
404
        return version_compare($this->getVersion(), '3.6', '>=');
405
    }
406
407
    public function supportsExtensions($extensions = array('partitions', 'tablespaces', 'clustering'))
408
    {
409
        if (isset($extensions['partitions'])) {
410
            return true;
411
        } elseif (isset($extensions['tablespaces'])) {
412
            return true;
413
        } elseif (isset($extensions['clustering'])) {
414
            return true;
415
        } else {
416
            return false;
417
        }
418
    }
419
420
    public function transactionStart($transaction_mode = false, $session_characteristics = false)
421
    {
422
        $this->query('BEGIN');
423
    }
424
425
    public function transactionSavepoint($savepoint)
426
    {
427
        $this->query("SAVEPOINT \"$savepoint\"");
428
    }
429
430
    public function transactionRollback($savepoint = false)
431
    {
432
        if ($savepoint) {
433
            $this->query("ROLLBACK TO $savepoint;");
434
        } else {
435
            $this->query('ROLLBACK;');
436
        }
437
    }
438
439
    public function transactionEnd($chain = false)
440
    {
441
        $this->query('COMMIT;');
442
    }
443
444
    public function clearTable($table)
445
    {
446
        $this->query("DELETE FROM \"$table\"");
447
    }
448
449
    public function comparisonClause($field, $value, $exact = false, $negate = false, $caseSensitive = null,
450
        $parameterised = false
451
    ) {
452
        if ($exact && !$caseSensitive) {
453
            $comp = ($negate) ? '!=' : '=';
454
        } else {
455
            if ($caseSensitive) {
456
                // GLOB uses asterisks as wildcards.
457
                // Replace them in search string, without replacing escaped percetage signs.
458
                $comp = 'GLOB';
459
                $value = preg_replace('/^%([^\\\\])/', '*$1', $value);
460
                $value = preg_replace('/([^\\\\])%$/', '$1*', $value);
461
                $value = preg_replace('/([^\\\\])%/', '$1*', $value);
462
            } else {
463
                $comp = 'LIKE';
464
            }
465
            if ($negate) {
466
                $comp = 'NOT ' . $comp;
467
            }
468
        }
469
470
        if ($parameterised) {
471
            return sprintf("%s %s ?", $field, $comp);
472
        } else {
473
            return sprintf("%s %s '%s'", $field, $comp, $value);
474
        }
475
    }
476
477
    public function formattedDatetimeClause($date, $format)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
478
    {
479
        preg_match_all('/%(.)/', $format, $matches);
480
        foreach ($matches[1] as $match) {
481
            if (array_search($match, array('Y', 'm', 'd', 'H', 'i', 's', 'U')) === false) {
482
                user_error('formattedDatetimeClause(): unsupported format character %' . $match, E_USER_WARNING);
483
            }
484
        }
485
486
        $translate = array(
487
            '/%i/' => '%M',
488
            '/%s/' => '%S',
489
            '/%U/' => '%s',
490
        );
491
        $format = preg_replace(array_keys($translate), array_values($translate), $format);
492
493
        $modifiers = array();
494
        if ($format == '%s' && $date != 'now') {
495
            $modifiers[] = 'utc';
496
        }
497
        if ($format != '%s' && $date == 'now') {
498
            $modifiers[] = 'localtime';
499
        }
500
501 View Code Duplication
        if (preg_match('/^now$/i', $date)) {
0 ignored issues
show
Duplication introduced by
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.

Loading history...
502
            $date = "'now'";
503
        } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date)) {
504
            $date = "'$date'";
505
        }
506
507
        $modifier = empty($modifiers) ? '' : ", '" . implode("', '", $modifiers) . "'";
508
        return "strftime('$format', $date$modifier)";
509
    }
510
511
    public function datetimeIntervalClause($date, $interval)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
512
    {
513
        $modifiers = array();
514
        if ($date == 'now') {
515
            $modifiers[] = 'localtime';
516
        }
517
518 View Code Duplication
        if (preg_match('/^now$/i', $date)) {
0 ignored issues
show
Duplication introduced by
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.

Loading history...
519
            $date = "'now'";
520
        } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date)) {
521
            $date = "'$date'";
522
        }
523
524
        $modifier = empty($modifiers) ? '' : ", '" . implode("', '", $modifiers) . "'";
525
        return "datetime($date$modifier, '$interval')";
526
    }
527
528
    public function datetimeDifferenceClause($date1, $date2)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
529
    {
530
        $modifiers1 = array();
531
        $modifiers2 = array();
532
533
        if ($date1 == 'now') {
534
            $modifiers1[] = 'localtime';
535
        }
536
        if ($date2 == 'now') {
537
            $modifiers2[] = 'localtime';
538
        }
539
540
        if (preg_match('/^now$/i', $date1)) {
541
            $date1 = "'now'";
542
        } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date1)) {
543
            $date1 = "'$date1'";
544
        }
545
546
        if (preg_match('/^now$/i', $date2)) {
547
            $date2 = "'now'";
548
        } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date2)) {
549
            $date2 = "'$date2'";
550
        }
551
552
        $modifier1 = empty($modifiers1) ? '' : ", '" . implode("', '", $modifiers1) . "'";
553
        $modifier2 = empty($modifiers2) ? '' : ", '" . implode("', '", $modifiers2) . "'";
554
555
        return "strftime('%s', $date1$modifier1) - strftime('%s', $date2$modifier2)";
556
    }
557
}
558