Completed
Push — develop ( 3414b7...190898 )
by John
03:53
created

checkTableNeedsUpdate()   D

Complexity

Conditions 14
Paths 337

Size

Total Lines 75
Code Lines 42

Duplication

Lines 75
Ratio 100 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 75
loc 75
rs 4
cc 14
eloc 42
nc 337
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Alpha\Model;
4
5
use Alpha\Model\Type\Integer;
6
use Alpha\Model\Type\Timestamp;
7
use Alpha\Model\Type\DEnum;
8
use Alpha\Model\Type\Relation;
9
use Alpha\Model\Type\RelationLookup;
10
use Alpha\Util\Config\ConfigProvider;
11
use Alpha\Util\Logging\Logger;
12
use Alpha\Util\Http\Session\SessionProviderFactory;
13
use Alpha\Exception\AlphaException;
14
use Alpha\Exception\FailedSaveException;
15
use Alpha\Exception\FailedDeleteException;
16
use Alpha\Exception\FailedIndexCreateException;
17
use Alpha\Exception\LockingException;
18
use Alpha\Exception\ValidationException;
19
use Alpha\Exception\CustomQueryException;
20
use Alpha\Exception\RecordNotFoundException;
21
use Alpha\Exception\BadTableNameException;
22
use Alpha\Exception\NotImplementedException;
23
use Alpha\Exception\PHPException;
24
use Exception;
25
use SQLite3Stmt;
26
use SQLite3;
27
use ReflectionClass;
28
29
/**
30
 * SQLite active record provider (uses the SQLite3 native API in PHP).
31
 *
32
 * @since 1.2
33
 *
34
 * @author John Collins <[email protected]>
35
 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
36
 * @copyright Copyright (c) 2017, John Collins (founder of Alpha Framework).
37
 * All rights reserved.
38
 *
39
 * <pre>
40
 * Redistribution and use in source and binary forms, with or
41
 * without modification, are permitted provided that the
42
 * following conditions are met:
43
 *
44
 * * Redistributions of source code must retain the above
45
 *   copyright notice, this list of conditions and the
46
 *   following disclaimer.
47
 * * Redistributions in binary form must reproduce the above
48
 *   copyright notice, this list of conditions and the
49
 *   following disclaimer in the documentation and/or other
50
 *   materials provided with the distribution.
51
 * * Neither the name of the Alpha Framework nor the names
52
 *   of its contributors may be used to endorse or promote
53
 *   products derived from this software without specific
54
 *   prior written permission.
55
 *
56
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
57
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
58
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
59
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
60
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
61
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
62
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
63
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
64
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
65
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
66
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
67
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
68
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
69
 * </pre>
70
 */
71
class ActiveRecordProviderSQLite implements ActiveRecordProviderInterface
72
{
73
    /**
74
     * Trace logger.
75
     *
76
     * @var \Alpha\Util\Logging\Logger
77
     *
78
     * @since 1.2
79
     */
80
    private static $logger = null;
81
82
    /**
83
     * Database connection.
84
     *
85
     * @var SQLite3
86
     *
87
     * @since 1.2
88
     */
89
    private static $connection;
90
91
    /**
92
     * The business object that we are mapping back to.
93
     *
94
     * @var \Alpha\Model\ActiveRecord
95
     *
96
     * @since 1.2
97
     */
98
    private $BO;
99
100
    /**
101
     * An array of new foreign keys that need to be created.
102
     *
103
     * @var array
104
     *
105
     * @since 2.0.1
106
     */
107
    private $foreignKeys = array();
108
109
    /**
110
     * The constructor.
111
     *
112
     * @since 1.2
113
     */
114
    public function __construct()
115
    {
116
        self::$logger = new Logger('ActiveRecordProviderSQLite');
117
        self::$logger->debug('>>__construct()');
118
119
        self::$logger->debug('<<__construct');
120
    }
121
122
    /**
123
     * (non-PHPdoc).
124
     *
125
     * @see Alpha\Model\ActiveRecordProviderInterface::getConnection()
126
     */
127
    public static function getConnection()
128
    {
129
        $config = ConfigProvider::getInstance();
130
131
        if (!isset(self::$connection)) {
132
            try {
133
                self::$connection = new SQLite3($config->get('db.file.path'));
134
            } catch (\Exception $e) {
135
                self::$logger->fatal('Could not open SQLite database: ['.$e->getMessage().']');
136
            }
137
        }
138
139
        return self::$connection;
140
    }
141
142
    /**
143
     * (non-PHPdoc).
144
     *
145
     * @see Alpha\Model\ActiveRecordProviderInterface::disconnect()
146
     */
147
    public static function disconnect()
148
    {
149
        if (isset(self::$connection)) {
150
            self::$connection->close();
151
            self::$connection = null;
152
        }
153
    }
154
155
    /**
156
     * (non-PHPdoc).
157
     *
158
     * @see Alpha\Model\ActiveRecordProviderInterface::getLastDatabaseError()
159
     */
160
    public static function getLastDatabaseError()
161
    {
162
        self::$connection->lastErrorMsg();
163
    }
164
165
    /**
166
     * (non-PHPdoc).
167
     *
168
     * @see Alpha\Model\ActiveRecordProviderInterface::query()
169
     */
170
    public function query($sqlQuery)
171
    {
172
        $this->BO->setLastQuery($sqlQuery);
173
174
        $resultArray = array();
175
176
        if (!$result = self::getConnection()->query($sqlQuery)) {
177
            throw new CustomQueryException('Failed to run the custom query, SQLite error is ['.self::getLastDatabaseError().'], query ['.$sqlQuery.']');
178
        } else {
179
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
180
                array_push($resultArray, $row);
181
            }
182
183
            return $resultArray;
184
        }
185
    }
186
187
    /**
188
     * (non-PHPdoc).
189
     *
190
     * @see Alpha\Model\ActiveRecordProviderInterface::load()
191
     */
192
    public function load($OID, $version = 0)
193
    {
194
        self::$logger->debug('>>load(OID=['.$OID.'], version=['.$version.'])');
195
196
        $attributes = $this->BO->getPersistentAttributes();
197
        $fields = '';
198
        foreach ($attributes as $att) {
199
            $fields .= $att.',';
200
        }
201
        $fields = mb_substr($fields, 0, -1);
202
203 View Code Duplication
        if ($version > 0) {
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...
204
            $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().'_history WHERE OID = :OID AND version_num = :version LIMIT 1;';
205
        } else {
206
            $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE OID = :OID LIMIT 1;';
207
        }
208
        $this->BO->setLastQuery($sqlQuery);
209
210
        try {
211
            $stmt = self::getConnection()->prepare($sqlQuery);
212
213
            $row = array();
214
215
            if ($version > 0) {
216
                $stmt->bindValue(':version', $version, SQLITE3_INTEGER);
217
            }
218
219
            $stmt->bindValue(':OID', $OID, SQLITE3_INTEGER);
220
221
            $result = $stmt->execute();
222
223
            // there should only ever be one (or none)
224
            $row = $result->fetchArray(SQLITE3_ASSOC);
225
226
            $stmt->close();
227
        } catch (PHPException $e) {
228
            self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
229 View Code Duplication
            if (!$this->BO->checkTableExists()) {
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...
230
                $this->BO->makeTable();
231
232
                throw new RecordNotFoundException('Failed to load object of OID ['.$OID.'], table ['.$this->BO->getTableName().'] did not exist so had to create!');
233
            }
234
235
            return;
236
        }
237
238 View Code Duplication
        if (!isset($row['OID']) || $row['OID'] < 1) {
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...
239
            self::$logger->debug('<<load');
240
            throw new RecordNotFoundException('Failed to load object of OID ['.$OID.'] not found in database.');
241
        }
242
243
        // get the class attributes
244
        $reflection = new ReflectionClass(get_class($this->BO));
245
        $properties = $reflection->getProperties();
246
247
        try {
248 View Code Duplication
            foreach ($properties as $propObj) {
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...
249
                $propName = $propObj->name;
250
251
                // filter transient attributes
252
                if (!in_array($propName, $this->BO->getTransientAttributes())) {
253
                    $this->BO->set($propName, $row[$propName]);
254
                } elseif (!$propObj->isPrivate() && $this->BO->getPropObject($propName) instanceof Relation) {
255
                    $prop = $this->BO->getPropObject($propName);
256
257
                    // handle the setting of ONE-TO-MANY relation values
258
                    if ($prop->getRelationType() == 'ONE-TO-MANY') {
259
                        $this->BO->set($propObj->name, $this->BO->getOID());
260
                    }
261
262
                    // handle the setting of MANY-TO-ONE relation values
263
                    if ($prop->getRelationType() == 'MANY-TO-ONE' && isset($row[$propName])) {
264
                        $this->BO->set($propObj->name, $row[$propName]);
265
                    }
266
                }
267
            }
268
        } catch (IllegalArguementException $e) {
0 ignored issues
show
Bug introduced by
The class Alpha\Model\IllegalArguementException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
269
            self::$logger->warn('Bad data stored in the table ['.$this->BO->getTableName().'], field ['.$propObj->name.'] bad value['.$row[$propObj->name].'], exception ['.$e->getMessage().']');
270
        } catch (PHPException $e) {
271
            // it is possible that the load failed due to the table not being up-to-date
272 View Code Duplication
            if ($this->BO->checkTableNeedsUpdate()) {
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...
273
                $missingFields = $this->BO->findMissingFields();
274
275
                $count = count($missingFields);
276
277
                for ($i = 0; $i < $count; ++$i) {
278
                    $this->BO->addProperty($missingFields[$i]);
279
                }
280
281
                self::$logger->debug('<<load');
282
                throw new RecordFoundException('Failed to load object of OID ['.$OID.'], table ['.$this->BO->getTableName().'] was out of sync with the database so had to be updated!');
283
            }
284
        }
285
286
        self::$logger->debug('<<load');
287
    }
288
289
    /**
290
     * (non-PHPdoc).
291
     *
292
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAllOldVersions()
293
     */
294
    public function loadAllOldVersions($OID)
295
    {
296
        self::$logger->debug('>>loadAllOldVersions(OID=['.$OID.'])');
297
298
        if (!$this->BO->getMaintainHistory()) {
299
            throw new RecordFoundException('loadAllOldVersions method called on an active record where no history is maintained!');
300
        }
301
302
        $sqlQuery = 'SELECT version_num FROM '.$this->BO->getTableName().'_history WHERE OID = \''.$OID.'\' ORDER BY version_num;';
303
304
        $this->BO->setLastQuery($sqlQuery);
305
306
        if (!$result = self::getConnection()->query($sqlQuery)) {
307
            self::$logger->debug('<<loadAllOldVersions');
308
            throw new RecordNotFoundException('Failed to load object versions, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
309
        }
310
311
        // now build an array of objects to be returned
312
        $objects = array();
313
        $count = 0;
314
        $RecordClass = get_class($this->BO);
315
316
        while ($row = $result->fetchArray()) {
317
            try {
318
                $obj = new $RecordClass();
319
                $obj->load($OID, $row['version_num']);
320
                $objects[$count] = $obj;
321
                ++$count;
322
            } catch (ResourceNotAllowedException $e) {
0 ignored issues
show
Bug introduced by
The class Alpha\Model\ResourceNotAllowedException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
323
                // the resource not allowed will be absent from the list
324
            }
325
        }
326
327
        self::$logger->warn('<<loadAllOldVersions ['.count($objects).']');
328
329
        return $objects;
330
    }
331
332
    /**
333
     * (non-PHPdoc).
334
     *
335
     * @see Alpha\Model\ActiveRecordProviderInterface::loadByAttribute()
336
     */
337
    public function loadByAttribute($attribute, $value, $ignoreClassType = false, $loadAttributes = array())
338
    {
339
        self::$logger->debug('>>loadByAttribute(attribute=['.$attribute.'], value=['.$value.'], ignoreClassType=['.$ignoreClassType.'], 
340
            loadAttributes=['.var_export($loadAttributes, true).'])');
341
342
        if (count($loadAttributes) == 0) {
343
            $attributes = $this->BO->getPersistentAttributes();
344
        } else {
345
            $attributes = $loadAttributes;
346
        }
347
348
        $fields = '';
349
        foreach ($attributes as $att) {
350
            $fields .= $att.',';
351
        }
352
        $fields = mb_substr($fields, 0, -1);
353
354 View Code Duplication
        if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
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...
355
            $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE '.$attribute.' = :attribute AND classname = :classname LIMIT 1;';
356
        } else {
357
            $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE '.$attribute.' = :attribute LIMIT 1;';
358
        }
359
360
        self::$logger->debug('Query=['.$sqlQuery.']');
361
362
        $this->BO->setLastQuery($sqlQuery);
363
        $stmt = self::getConnection()->prepare($sqlQuery);
364
365
        $row = array();
0 ignored issues
show
Unused Code introduced by
$row 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...
366
367
        if ($stmt instanceof SQLite3Stmt) {
368
            if ($this->BO->getPropObject($attribute) instanceof Integer) {
369 View Code Duplication
                if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
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...
370
                    $stmt->bindValue(':attribute', $value, SQLITE3_INTEGER);
371
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
372
                } else {
373
                    $stmt->bindValue(':attribute', $value, SQLITE3_INTEGER);
374
                }
375 View Code Duplication
            } else {
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...
376
                if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
377
                    $stmt->bindValue(':attribute', $value, SQLITE3_TEXT);
378
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
379
                } else {
380
                    $stmt->bindValue(':attribute', $value, SQLITE3_TEXT);
381
                }
382
            }
383
384
            $result = $stmt->execute();
385
386
            // there should only ever be one (or none)
387
            $row = $result->fetchArray(SQLITE3_ASSOC);
388
389
            $stmt->close();
390
        } else {
391
            self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
392
            if (!$this->BO->checkTableExists()) {
393
                $this->BO->makeTable();
394
395
                throw new RecordNotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], table did not exist so had to create!');
396
            }
397
398
            return;
399
        }
400
401 View Code Duplication
        if (!isset($row['OID']) || $row['OID'] < 1) {
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...
402
            self::$logger->debug('<<loadByAttribute');
403
            throw new RecordNotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], not found in database.');
404
        }
405
406
        $this->OID = $row['OID'];
0 ignored issues
show
Bug introduced by
The property OID does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
407
408
        // get the class attributes
409
        $reflection = new ReflectionClass(get_class($this->BO));
410
        $properties = $reflection->getProperties();
411
412
        try {
413 View Code Duplication
            foreach ($properties as $propObj) {
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...
414
                $propName = $propObj->name;
415
416
                if (isset($row[$propName])) {
417
                    // filter transient attributes
418
                    if (!in_array($propName, $this->BO->getTransientAttributes())) {
419
                        $this->BO->set($propName, $row[$propName]);
420
                    } elseif (!$propObj->isPrivate() && $this->BO->get($propName) != '' && $this->BO->getPropObject($propName) instanceof Relation) {
421
                        $prop = $this->BO->getPropObject($propName);
422
423
                        // handle the setting of ONE-TO-MANY relation values
424
                        if ($prop->getRelationType() == 'ONE-TO-MANY') {
425
                            $this->BO->set($propObj->name, $this->BO->getOID());
426
                        }
427
                    }
428
                }
429
            }
430
        } catch (IllegalArguementException $e) {
0 ignored issues
show
Bug introduced by
The class Alpha\Model\IllegalArguementException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
431
            self::$logger->warn('Bad data stored in the table ['.$this->BO->getTableName().'], field ['.$propObj->name.'] bad value['.$row[$propObj->name].'], exception ['.$e->getMessage().']');
432
        } catch (PHPException $e) {
433
            // it is possible that the load failed due to the table not being up-to-date
434 View Code Duplication
            if ($this->BO->checkTableNeedsUpdate()) {
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...
435
                $missingFields = $this->BO->findMissingFields();
436
437
                $count = count($missingFields);
438
439
                for ($i = 0; $i < $count; ++$i) {
440
                    $this->BO->addProperty($missingFields[$i]);
441
                }
442
443
                self::$logger->debug('<<loadByAttribute');
444
                throw new RecordNotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], table ['.$this->BO->getTableName().'] was out of sync with the database so had to be updated!');
445
            }
446
        }
447
448
        self::$logger->debug('<<loadByAttribute');
449
    }
450
451
    /**
452
     * (non-PHPdoc).
453
     *
454
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAll()
455
     */
456 View Code Duplication
    public function loadAll($start = 0, $limit = 0, $orderBy = 'OID', $order = 'ASC', $ignoreClassType = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
457
    {
458
        self::$logger->debug('>>loadAll(start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
459
460
        // ensure that the field name provided in the orderBy param is legit
461
        try {
462
            $field = $this->BO->get($orderBy);
0 ignored issues
show
Unused Code introduced by
$field 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...
463
        } catch (AlphaException $e) {
464
            throw new AlphaException('The field name ['.$orderBy.'] provided in the param orderBy does not exist on the class ['.get_class($this->BO).']');
465
        }
466
467
        if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
468
            if ($limit == 0) {
469
                $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE classname = \''.addslashes(get_class($this->BO)).'\' ORDER BY '.$orderBy.' '.$order.';';
470
            } else {
471
                $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE classname = \''.addslashes(get_class($this->BO)).'\' ORDER BY '.$orderBy.' '.$order.' LIMIT '.
472
                    $limit.' OFFSET '.$start.';';
473
            }
474
        } else {
475
            if ($limit == 0) {
476
                $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' ORDER BY '.$orderBy.' '.$order.';';
477
            } else {
478
                $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' ORDER BY '.$orderBy.' '.$order.' LIMIT '.$limit.' OFFSET '.$start.';';
479
            }
480
        }
481
482
        $this->BO->setLastQuery($sqlQuery);
483
484
        if (!$result = self::getConnection()->query($sqlQuery)) {
485
            self::$logger->debug('<<loadAll');
486
            throw new RecordNotFoundException('Failed to load object OIDs, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
487
        }
488
489
        // now build an array of objects to be returned
490
        $objects = array();
491
        $count = 0;
492
        $RecordClass = get_class($this->BO);
493
494
        while ($row = $result->fetchArray()) {
495
            try {
496
                $obj = new $RecordClass();
497
                $obj->load($row['OID']);
498
                $objects[$count] = $obj;
499
                ++$count;
500
            } catch (ResourceNotAllowedException $e) {
0 ignored issues
show
Bug introduced by
The class Alpha\Model\ResourceNotAllowedException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
501
                // the resource not allowed will be absent from the list
502
            }
503
        }
504
505
        self::$logger->debug('<<loadAll ['.count($objects).']');
506
507
        return $objects;
508
    }
509
510
    /**
511
     * (non-PHPdoc).
512
     *
513
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAllByAttribute()
514
     */
515
    public function loadAllByAttribute($attribute, $value, $start = 0, $limit = 0, $orderBy = 'OID', $order = 'ASC', $ignoreClassType = false, $constructorArgs = array())
516
    {
517
        self::$logger->debug('>>loadAllByAttribute(attribute=['.$attribute.'], value=['.$value.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.'], constructorArgs=['.print_r($constructorArgs, true).']');
518
519
        if ($start != 0 && $limit != 0) {
520
            $limit = ' LIMIT '.$limit.' OFFSET '.$start.';';
521
        } else {
522
            $limit = ';';
523
        }
524
525 View Code Duplication
        if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
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...
526
            $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName()." WHERE $attribute = :attribute AND classname = :classname ORDER BY ".$orderBy.' '.$order.$limit;
527
        } else {
528
            $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName()." WHERE $attribute = :attribute ORDER BY ".$orderBy.' '.$order.$limit;
529
        }
530
531
        $this->BO->setLastQuery($sqlQuery);
532
        self::$logger->debug($sqlQuery);
533
534
        $stmt = self::getConnection()->prepare($sqlQuery);
535
536
        $objects = array();
537
538
        if ($stmt instanceof SQLite3Stmt) {
539
            if ($this->BO->getPropObject($attribute) instanceof Integer) {
540 View Code Duplication
                if ($this->BO->isTableOverloaded()) {
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...
541
                    $stmt->bindValue(':attribute', $value, SQLITE3_INTEGER);
542
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
543
                } else {
544
                    $stmt->bindValue(':attribute', $value, SQLITE3_INTEGER);
545
                }
546 View Code Duplication
            } else {
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...
547
                if ($this->BO->isTableOverloaded()) {
548
                    $stmt->bindValue(':attribute', $value, SQLITE3_TEXT);
549
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
550
                } else {
551
                    $stmt->bindValue(':attribute', $value, SQLITE3_TEXT);
552
                }
553
            }
554
555
            $result = $stmt->execute();
556
557
            // now build an array of objects to be returned
558
            $count = 0;
559
            $RecordClass = get_class($this->BO);
560
561 View Code Duplication
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
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...
562
                try {
563
                    $argsCount = count($constructorArgs);
564
565
                    if ($argsCount < 1) {
566
                        $obj = new $RecordClass();
567
                    } else {
568
                        switch ($argsCount) {
569
                            case 1:
570
                                $obj = new $RecordClass($constructorArgs[0]);
571
                                break;
572
                            case 2:
573
                                $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1]);
574
                                break;
575
                            case 3:
576
                                $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2]);
577
                                break;
578
                            case 4:
579
                                $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2], $constructorArgs[3]);
580
                                break;
581
                            case 5:
582
                                $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2], $constructorArgs[3], $constructorArgs[4]);
583
                                break;
584
                            default:
585
                                throw new IllegalArguementException('Too many elements in the $constructorArgs array passed to the loadAllByAttribute method!');
586
                        }
587
                    }
588
589
                    $obj->load($row['OID']);
590
                    $objects[$count] = $obj;
591
                    ++$count;
592
                } catch (ResourceNotAllowedException $e) {
0 ignored issues
show
Bug introduced by
The class Alpha\Model\ResourceNotAllowedException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
593
                    // the resource not allowed will be absent from the list
594
                }
595
            }
596
597
            $stmt->close();
598 View Code Duplication
        } else {
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...
599
            self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
600
601
            if (!$this->BO->checkTableExists()) {
602
                $this->BO->makeTable();
603
604
                throw new RecordFoundException('Failed to load objects by attribute ['.$attribute.'] and value ['.$value.'], table did not exist so had to create!');
605
            }
606
607
            self::$logger->debug('<<loadAllByAttribute []');
608
609
            return array();
610
        }
611
612
        self::$logger->debug('<<loadAllByAttribute ['.count($objects).']');
613
614
        return $objects;
615
    }
616
617
    /**
618
     * (non-PHPdoc).
619
     *
620
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAllByAttributes()
621
     */
622
    public function loadAllByAttributes($attributes = array(), $values = array(), $start = 0, $limit = 0, $orderBy = 'OID', $order = 'ASC', $ignoreClassType = false, $constructorArgs = array())
623
    {
624
        self::$logger->debug('>>loadAllByAttributes(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'], start=['.
625
            $start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.'], constructorArgs=['.print_r($constructorArgs, true).']');
626
627
        $whereClause = ' WHERE';
628
629
        $count = count($attributes);
630
631
        for ($i = 0; $i < $count; ++$i) {
632
            $whereClause .= ' '.$attributes[$i].' = :'.$attributes[$i].' AND';
633
            self::$logger->debug($whereClause);
634
        }
635
636
        if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
637
            $whereClause .= ' classname = :classname AND';
638
        }
639
640
        // remove the last " AND"
641
        $whereClause = mb_substr($whereClause, 0, -4);
642
643
        if ($limit != 0) {
644
            $limit = ' LIMIT '.$limit.' OFFSET '.$start.';';
645
        } else {
646
            $limit = ';';
647
        }
648
649
        $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().$whereClause.' ORDER BY '.$orderBy.' '.$order.$limit;
650
651
        $this->BO->setLastQuery($sqlQuery);
652
653
        $stmt = self::getConnection()->prepare($sqlQuery);
654
655
        if ($stmt instanceof SQLite3Stmt) {
656
            // bind params where required attributes are provided
657
            if (count($attributes) > 0 && count($attributes) == count($values)) {
658
                for ($i = 0; $i < count($attributes); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
659
                    if (strcspn($values[$i], '0123456789') != strlen($values[$i])) {
660
                        $stmt->bindValue(':'.$attributes[$i], $values[$i], SQLITE3_INTEGER);
661
                    } else {
662
                        $stmt->bindValue(':'.$attributes[$i], $values[$i], SQLITE3_TEXT);
663
                    }
664
                }
665
            } else {
666
                // we'll still need to bind the "classname" for overloaded BOs...
667
                if ($this->BO->isTableOverloaded()) {
668
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
669
                }
670
            }
671
672
            $result = $stmt->execute();
673 View Code Duplication
        } else {
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...
674
            self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
675
676
            if (!$this->BO->checkTableExists()) {
677
                $this->BO->makeTable();
678
679
                throw new RecordFoundException('Failed to load objects by attributes ['.var_export($attributes, true).'] and values ['.
680
                    var_export($values, true).'], table did not exist so had to create!');
681
            }
682
683
            self::$logger->debug('<<loadAllByAttributes []');
684
685
            return array();
686
        }
687
688
        // now build an array of objects to be returned
689
        $objects = array();
690
        $count = 0;
691
        $RecordClass = get_class($this->BO);
692
693 View Code Duplication
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
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...
694
            try {
695
                $argsCount = count($constructorArgs);
696
697
                if ($argsCount < 1) {
698
                    $obj = new $RecordClass();
699
                } else {
700
                    switch ($argsCount) {
701
                        case 1:
702
                            $obj = new $RecordClass($constructorArgs[0]);
703
                            break;
704
                        case 2:
705
                            $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1]);
706
                            break;
707
                        case 3:
708
                            $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2]);
709
                            break;
710
                        case 4:
711
                            $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2], $constructorArgs[3]);
712
                            break;
713
                        case 5:
714
                            $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2], $constructorArgs[3], $constructorArgs[4]);
715
                            break;
716
                        default:
717
                            throw new IllegalArguementException('Too many elements in the $constructorArgs array passed to the loadAllByAttribute method!');
718
                    }
719
                }
720
721
                $obj->load($row['OID']);
722
                $objects[$count] = $obj;
723
                ++$count;
724
            } catch (ResourceNotAllowedException $e) {
0 ignored issues
show
Bug introduced by
The class Alpha\Model\ResourceNotAllowedException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
725
                // the resource not allowed will be absent from the list
726
            }
727
        }
728
729
        $stmt->close();
730
731
        self::$logger->debug('<<loadAllByAttributes ['.count($objects).']');
732
733
        return $objects;
734
    }
735
736
    /**
737
     * (non-PHPdoc).
738
     *
739
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAllByDayUpdated()
740
     */
741 View Code Duplication
    public function loadAllByDayUpdated($date, $start = 0, $limit = 0, $orderBy = 'OID', $order = 'ASC', $ignoreClassType = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
742
    {
743
        self::$logger->debug('>>loadAllByDayUpdated(date=['.$date.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
744
745
        if ($start != 0 && $limit != 0) {
746
            $limit = ' LIMIT '.$limit.' OFFSET '.$start.';';
747
        } else {
748
            $limit = ';';
749
        }
750
751
        if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
752
            $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName()." WHERE updated_ts >= '".$date." 00:00:00' AND updated_ts <= '".$date." 23:59:59' AND classname = '".addslashes(get_class($this->BO))."' ORDER BY ".$orderBy.' '.$order.$limit;
753
        } else {
754
            $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName()." WHERE updated_ts >= '".$date." 00:00:00' AND updated_ts <= '".$date." 23:59:59' ORDER BY ".$orderBy.' '.$order.$limit;
755
        }
756
757
        $this->BO->setLastQuery($sqlQuery);
758
759
        if (!$result = self::getConnection()->query($sqlQuery)) {
760
            self::$logger->debug('<<loadAllByDayUpdated');
761
            throw new RecordNotFoundException('Failed to load object OIDs, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
762
        }
763
764
        // now build an array of objects to be returned
765
        $objects = array();
766
        $count = 0;
767
        $RecordClass = get_class($this->BO);
768
769
        while ($row = $result->fetchArray()) {
770
            $obj = new $RecordClass();
771
            $obj->load($row['OID']);
772
            $objects[$count] = $obj;
773
            ++$count;
774
        }
775
776
        self::$logger->debug('<<loadAllByDayUpdated ['.count($objects).']');
777
778
        return $objects;
779
    }
780
781
    /**
782
     * (non-PHPdoc).
783
     *
784
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAllFieldValuesByAttribute()
785
     */
786
    public function loadAllFieldValuesByAttribute($attribute, $value, $returnAttribute, $order = 'ASC', $ignoreClassType = false)
787
    {
788
        self::$logger->debug('>>loadAllFieldValuesByAttribute(attribute=['.$attribute.'], value=['.$value.'], returnAttribute=['.$returnAttribute.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
789
790
        if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
791
            $sqlQuery = 'SELECT '.$returnAttribute.' FROM '.$this->BO->getTableName()." WHERE $attribute = '$value' AND classname = '".addslashes(get_class($this->BO))."' ORDER BY OID ".$order.';';
792
        } else {
793
            $sqlQuery = 'SELECT '.$returnAttribute.' FROM '.$this->BO->getTableName()." WHERE $attribute = '$value' ORDER BY OID ".$order.';';
794
        }
795
796
        $this->BO->setLastQuery($sqlQuery);
797
798
        self::$logger->debug('lastQuery ['.$sqlQuery.']');
799
800
        if (!$result = self::getConnection()->query($sqlQuery)) {
801
            self::$logger->debug('<<loadAllFieldValuesByAttribute');
802
            throw new RecordNotFoundException('Failed to load field ['.$returnAttribute.'] values, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
803
        }
804
805
        // now build an array of attribute values to be returned
806
        $values = array();
807
        $count = 0;
808
        $RecordClass = get_class($this->BO);
0 ignored issues
show
Unused Code introduced by
$RecordClass 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...
809
810
        while ($row = $result->fetchArray()) {
811
            $values[$count] = $row[$returnAttribute];
812
            ++$count;
813
        }
814
815
        self::$logger->debug('<<loadAllFieldValuesByAttribute ['.count($values).']');
816
817
        return $values;
818
    }
819
820
    /**
821
     * (non-PHPdoc).
822
     *
823
     * @see Alpha\Model\ActiveRecordProviderInterface::save()
824
     */
825
    public function save()
826
    {
827
        self::$logger->debug('>>save()');
828
829
        $config = ConfigProvider::getInstance();
830
        $sessionProvider = $config->get('session.provider.name');
831
        $session = SessionProviderFactory::getInstance($sessionProvider);
832
833
        // get the class attributes
834
        $reflection = new ReflectionClass(get_class($this->BO));
835
        $properties = $reflection->getProperties();
836
837
        if ($this->BO->getVersion() != $this->BO->getVersionNumber()->getValue()) {
838
            throw new LockingException('Could not save the object as it has been updated by another user.  Please try saving again.');
839
        }
840
841
        // set the "updated by" fields, we can only set the user id if someone is logged in
842
        if ($session->get('currentUser') != null) {
843
            $this->BO->set('updated_by', $session->get('currentUser')->getOID());
844
        }
845
846
        $this->BO->set('updated_ts', new Timestamp(date('Y-m-d H:i:s')));
847
848
        // check to see if it is a transient object that needs to be inserted
849
        if ($this->BO->isTransient()) {
850
            $savedFields = array();
851
            $sqlQuery = 'INSERT INTO '.$this->BO->getTableName().' (';
852
853 View Code Duplication
            foreach ($properties as $propObj) {
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...
854
                $propName = $propObj->name;
855
                if (!in_array($propName, $this->BO->getTransientAttributes())) {
856
                    // Skip the OID, database auto number takes care of this.
857
                    if ($propName != 'OID' && $propName != 'version_num') {
858
                        $sqlQuery .= "$propName,";
859
                        $savedFields[] = $propName;
860
                    }
861
862
                    if ($propName == 'version_num') {
863
                        $sqlQuery .= 'version_num,';
864
                        $savedFields[] = 'version_num';
865
                    }
866
                }
867
            }
868
            if ($this->BO->isTableOverloaded()) {
869
                $sqlQuery .= 'classname,';
870
            }
871
872
            $sqlQuery = rtrim($sqlQuery, ',');
873
874
            $sqlQuery .= ') VALUES (';
875
876
            foreach ($savedFields as $savedField) {
877
                $sqlQuery .= ':'.$savedField.',';
878
            }
879
880
            if ($this->BO->isTableOverloaded()) {
881
                $sqlQuery .= ':classname,';
882
            }
883
884
            $sqlQuery = rtrim($sqlQuery, ',').')';
885
886
            $this->BO->setLastQuery($sqlQuery);
887
            self::$logger->debug('Query ['.$sqlQuery.']');
888
889
            $stmt = self::getConnection()->prepare($sqlQuery);
890
891
            if ($stmt instanceof SQLite3Stmt) {
892 View Code Duplication
                foreach ($savedFields as $savedField) {
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...
893
                    if ($this->BO->get($savedField) instanceof Integer) {
894
                        $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_INTEGER);
895
                    } else {
896
                        $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_TEXT);
897
                    }
898
                }
899
900
                if ($this->BO->isTableOverloaded()) {
901
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
902
                }
903
904
                $stmt->bindValue(':version_num', 1, SQLITE3_INTEGER); // on an initial save, this will always be 1
905
                $this->BO->set('version_num', 1);
906
907
                try {
908
                    $stmt->execute();
909
                } catch (Exception $e) {
910
                    if (self::getConnection()->lastErrorCode() == 19) {
911
                        throw new ValidationException('Unique key violation while trying to save object, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
912 View Code Duplication
                    } else {
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...
913
                        throw new FailedSaveException('Failed to save object, exception ['.$e->getMessage().'], DB error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
914
                    }
915
                }
916 View Code Duplication
            } else {
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...
917
                throw new FailedSaveException('Failed to save object, exception ['.$e->getMessage().'], DB error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
918
            }
919
        } else {
920
            // assume that it is a persistent object that needs to be updated
921
            $savedFields = array();
922
            $sqlQuery = 'UPDATE '.$this->BO->getTableName().' SET ';
923
924 View Code Duplication
            foreach ($properties as $propObj) {
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...
925
                $propName = $propObj->name;
926
                if (!in_array($propName, $this->BO->getTransientAttributes())) {
927
                    // Skip the OID, database auto number takes care of this.
928
                    if ($propName != 'OID' && $propName != 'version_num') {
929
                        $sqlQuery .= "$propName = :$propName,";
930
                        $savedFields[] = $propName;
931
                    }
932
933
                    if ($propName == 'version_num') {
934
                        $sqlQuery .= 'version_num = :version_num,';
935
                        $savedFields[] = 'version_num';
936
                    }
937
                }
938
            }
939
940
            if ($this->BO->isTableOverloaded()) {
941
                $sqlQuery .= 'classname = :classname,';
942
            }
943
944
            $sqlQuery = rtrim($sqlQuery, ',');
945
946
            $sqlQuery .= ' WHERE OID=:OID;';
947
948
            $this->BO->setLastQuery($sqlQuery);
949
            $stmt = self::getConnection()->prepare($sqlQuery);
950
951
            if ($stmt instanceof SQLite3Stmt) {
952 View Code Duplication
                foreach ($savedFields as $savedField) {
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...
953
                    if ($this->BO->get($savedField) instanceof Integer) {
954
                        $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_INTEGER);
955
                    } else {
956
                        $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_TEXT);
957
                    }
958
                }
959
960
                if ($this->BO->isTableOverloaded()) {
961
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
962
                }
963
964
                $stmt->bindValue(':OID', $this->BO->getOID(), SQLITE3_INTEGER);
965
966
                $temp = $this->BO->getVersionNumber()->getValue();
967
                $this->BO->set('version_num', $temp + 1);
968
                $stmt->bindValue(':version_num', $temp + 1, SQLITE3_INTEGER);
969
970
                $stmt->execute();
971
            } else {
972
                throw new FailedSaveException('Failed to save object, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
973
            }
974
        }
975
976
        if ($stmt != null && $stmt != false) {
977
            // populate the updated OID in case we just done an insert
978
            if ($this->BO->isTransient()) {
979
                $this->BO->setOID(self::getConnection()->lastInsertRowID());
980
            }
981
982
            try {
983 View Code Duplication
                foreach ($properties as $propObj) {
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...
984
                    $propName = $propObj->name;
985
986
                    if ($this->BO->getPropObject($propName) instanceof Relation) {
987
                        $prop = $this->BO->getPropObject($propName);
988
989
                        // handle the saving of MANY-TO-MANY relation values
990
                        if ($prop->getRelationType() == 'MANY-TO-MANY' && count($prop->getRelatedOIDs()) > 0) {
991
                            try {
992
                                try {
993
                                    // check to see if the rel is on this class
994
                                    $side = $prop->getSide(get_class($this->BO));
995
                                } catch (IllegalArguementException $iae) {
0 ignored issues
show
Bug introduced by
The class Alpha\Model\IllegalArguementException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
996
                                    $side = $prop->getSide(get_parent_class($this->BO));
997
                                }
998
999
                                $lookUp = $prop->getLookup();
1000
1001
                                // first delete all of the old RelationLookup objects for this rel
1002
                                try {
1003
                                    if ($side == 'left') {
1004
                                        $lookUp->deleteAllByAttribute('leftID', $this->BO->getOID());
1005
                                    } else {
1006
                                        $lookUp->deleteAllByAttribute('rightID', $this->BO->getOID());
1007
                                    }
1008
                                } catch (Exception $e) {
1009
                                    throw new FailedSaveException('Failed to delete old RelationLookup objects on the table ['.$prop->getLookup()->getTableName().'], error is ['.$e->getMessage().']');
1010
                                }
1011
1012
                                $OIDs = $prop->getRelatedOIDs();
1013
1014
                                if (isset($OIDs) && !empty($OIDs[0])) {
1015
                                    // now for each posted OID, create a new RelationLookup record and save
1016
                                    foreach ($OIDs as $oid) {
1017
                                        $newLookUp = new RelationLookup($lookUp->get('leftClassName'), $lookUp->get('rightClassName'));
1018
                                        if ($side == 'left') {
1019
                                            $newLookUp->set('leftID', $this->BO->getOID());
1020
                                            $newLookUp->set('rightID', $oid);
1021
                                        } else {
1022
                                            $newLookUp->set('rightID', $this->BO->getOID());
1023
                                            $newLookUp->set('leftID', $oid);
1024
                                        }
1025
                                        $newLookUp->save();
1026
                                    }
1027
                                }
1028
                            } catch (Exception $e) {
1029
                                throw new FailedSaveException('Failed to update a MANY-TO-MANY relation on the object, error is ['.$e->getMessage().']');
1030
1031
                                return;
0 ignored issues
show
Unused Code introduced by
return; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1032
                            }
1033
                        }
1034
1035
                        // handle the saving of ONE-TO-MANY relation values
1036
                        if ($prop->getRelationType() == 'ONE-TO-MANY') {
1037
                            $prop->setValue($this->BO->getOID());
1038
                        }
1039
                    }
1040
                }
1041
            } catch (Exception $e) {
1042
                throw new FailedSaveException('Failed to save object, error is ['.$e->getMessage().']');
1043
            }
1044
1045
            $stmt->close();
1046
        } else {
1047
            // there has been an error, so decrement the version number back
1048
            $temp = $this->BO->getVersionNumber()->getValue();
1049
            $this->BO->set('version_num', $temp - 1);
1050
1051
            throw new FailedSaveException('Failed to save object, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
1052
        }
1053
1054
        if ($this->BO->getMaintainHistory()) {
1055
            $this->BO->saveHistory();
1056
        }
1057
    }
1058
1059
    /**
1060
     * (non-PHPdoc).
1061
     *
1062
     * @see Alpha\Model\ActiveRecordProviderInterface::saveAttribute()
1063
     */
1064
    public function saveAttribute($attribute, $value)
1065
    {
1066
        self::$logger->debug('>>saveAttribute(attribute=['.$attribute.'], value=['.$value.'])');
1067
1068
        // assume that it is a persistent object that needs to be updated
1069
        $sqlQuery = 'UPDATE '.$this->BO->getTableName().' SET '.$attribute.'=:attribute, version_num =:version WHERE OID=:OID;';
1070
1071
        $this->BO->setLastQuery($sqlQuery);
1072
        $stmt = self::getConnection()->prepare($sqlQuery);
1073
1074
        $newVersionNumber = $this->BO->getVersionNumber()->getValue() + 1;
1075
1076
        if ($stmt instanceof SQLite3Stmt) {
1077
            if ($this->BO->getPropObject($attribute) instanceof Integer) {
1078
                $stmt->bindValue(':attribute', $value, SQLITE3_INTEGER);
1079
            } else {
1080
                $stmt->bindValue(':attribute', $value, SQLITE3_TEXT);
1081
            }
1082
1083
            $stmt->bindValue(':version', $newVersionNumber, SQLITE3_INTEGER);
1084
            $stmt->bindValue(':OID', $this->BO->getOID(), SQLITE3_INTEGER);
1085
1086
            $stmt->execute();
1087 View Code Duplication
        } else {
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...
1088
            throw new FailedSaveException('Failed to save attribute, error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
1089
        }
1090
1091
        $stmt->close();
1092
1093
        $this->BO->set($attribute, $value);
1094
        $this->BO->set('version_num', $newVersionNumber);
1095
1096
        if ($this->BO->getMaintainHistory()) {
1097
            $this->BO->saveHistory();
1098
        }
1099
1100
        self::$logger->debug('<<saveAttribute');
1101
    }
1102
1103
    /**
1104
     * (non-PHPdoc).
1105
     *
1106
     * @see Alpha\Model\ActiveRecordProviderInterface::saveHistory()
1107
     */
1108
    public function saveHistory()
1109
    {
1110
        self::$logger->debug('>>saveHistory()');
1111
1112
        // get the class attributes
1113
        $reflection = new ReflectionClass(get_class($this->BO));
1114
        $properties = $reflection->getProperties();
1115
1116
        $savedFields = array();
1117
        $attributeNames = array();
1118
        $attributeValues = array();
1119
1120
        $sqlQuery = 'INSERT INTO '.$this->BO->getTableName().'_history (';
1121
1122 View Code Duplication
        foreach ($properties as $propObj) {
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...
1123
            $propName = $propObj->name;
1124
            if (!in_array($propName, $this->BO->getTransientAttributes())) {
1125
                $sqlQuery .= "$propName,";
1126
                $attributeNames[] = $propName;
1127
                $attributeValues[] = $this->BO->get($propName);
1128
                $savedFields[] = $propName;
1129
            }
1130
        }
1131
1132
        if ($this->BO->isTableOverloaded()) {
1133
            $sqlQuery .= 'classname,';
1134
        }
1135
1136
        $sqlQuery = rtrim($sqlQuery, ',');
1137
1138
        $sqlQuery .= ') VALUES (';
1139
1140
        foreach ($savedFields as $savedField) {
1141
            $sqlQuery .= ':'.$savedField.',';
1142
        }
1143
1144
        if ($this->BO->isTableOverloaded()) {
1145
            $sqlQuery .= ':classname,';
1146
        }
1147
1148
        $sqlQuery = rtrim($sqlQuery, ',').')';
1149
1150
        $this->BO->setLastQuery($sqlQuery);
1151
        self::$logger->debug('Query ['.$sqlQuery.']');
1152
1153
        $stmt = self::getConnection()->prepare($sqlQuery);
1154
1155
        if ($stmt instanceof SQLite3Stmt) {
1156 View Code Duplication
            foreach ($savedFields as $savedField) {
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...
1157
                if ($this->BO->get($savedField) instanceof Integer) {
1158
                    $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_INTEGER);
1159
                } else {
1160
                    $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_TEXT);
1161
                }
1162
            }
1163
1164
            if ($this->BO->isTableOverloaded()) {
1165
                $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
1166
            }
1167
1168
            $stmt->execute();
1169 View Code Duplication
        } else {
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...
1170
            throw new FailedSaveException('Failed to save object history, error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
1171
        }
1172
    }
1173
1174
    /**
1175
     * (non-PHPdoc).
1176
     *
1177
     * @see Alpha\Model\ActiveRecordProviderInterface::delete()
1178
     */
1179
    public function delete()
1180
    {
1181
        self::$logger->debug('>>delete()');
1182
1183
        $sqlQuery = 'DELETE FROM '.$this->BO->getTableName().' WHERE OID = :OID;';
1184
1185
        $this->BO->setLastQuery($sqlQuery);
1186
1187
        $stmt = self::getConnection()->prepare($sqlQuery);
1188
1189
        if ($stmt instanceof SQLite3Stmt) {
1190
            $stmt->bindValue(':OID', $this->BO->getOID(), SQLITE3_INTEGER);
1191
            $stmt->execute();
1192
            self::$logger->debug('Deleted the object ['.$this->BO->getOID().'] of class ['.get_class($this->BO).']');
1193 View Code Duplication
        } else {
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...
1194
            throw new FailedDeleteException('Failed to delete object ['.$this->BO->getOID().'], error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
1195
        }
1196
1197
        $stmt->close();
1198
1199
        self::$logger->debug('<<delete');
1200
    }
1201
1202
    /**
1203
     * (non-PHPdoc).
1204
     *
1205
     * @see Alpha\Model\ActiveRecordProviderInterface::getVersion()
1206
     */
1207
    public function getVersion()
1208
    {
1209
        self::$logger->debug('>>getVersion()');
1210
1211
        $sqlQuery = 'SELECT version_num FROM '.$this->BO->getTableName().' WHERE OID = :OID;';
1212
        $this->BO->setLastQuery($sqlQuery);
1213
1214
        $stmt = self::getConnection()->prepare($sqlQuery);
1215
1216
        if ($stmt instanceof SQLite3Stmt) {
1217
            $stmt->bindValue(':OID', $this->BO->getOID(), SQLITE3_INTEGER);
1218
1219
            $result = $stmt->execute();
1220
1221
            // there should only ever be one (or none)
1222
            $row = $result->fetchArray(SQLITE3_ASSOC);
1223
1224
            $stmt->close();
1225 View Code Duplication
        } else {
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...
1226
            self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
1227
            if (!$this->BO->checkTableExists()) {
1228
                $this->BO->makeTable();
1229
1230
                throw new RecordNotFoundException('Failed to get the version number, table did not exist so had to create!');
1231
            }
1232
1233
            return;
1234
        }
1235
1236 View Code Duplication
        if (!isset($row['version_num']) || $row['version_num'] < 1) {
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...
1237
            self::$logger->debug('<<getVersion [0]');
1238
1239
            return 0;
1240
        } else {
1241
            $version_num = $row['version_num'];
1242
1243
            self::$logger->debug('<<getVersion ['.$version_num.']');
1244
1245
            return $version_num;
1246
        }
1247
    }
1248
1249
    /**
1250
     * (non-PHPdoc).
1251
     *
1252
     * @see Alpha\Model\ActiveRecordProviderInterface::makeTable()
1253
     */
1254
    public function makeTable()
1255
    {
1256
        self::$logger->debug('>>makeTable()');
1257
1258
        $sqlQuery = 'CREATE TABLE '.$this->BO->getTableName().' (OID INTEGER PRIMARY KEY,';
1259
1260
        // get the class attributes
1261
        $reflection = new ReflectionClass(get_class($this->BO));
1262
        $properties = $reflection->getProperties();
1263
1264
        $foreignKeys = array();
1265
1266
        foreach ($properties as $propObj) {
1267
            $propName = $propObj->name;
1268
1269
            if (!in_array($propName, $this->BO->getTransientAttributes()) && $propName != 'OID') {
1270
                $propReflect = new ReflectionClass($this->BO->getPropObject($propName));
1271
                $propClass = $propReflect->getShortName();
1272
1273
                switch (mb_strtoupper($propClass)) {
1274
                    case 'INTEGER':
1275
                        // special properties for RelationLookup OIDs
1276
                        if ($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID')) {
1277
                            $sqlQuery .= "$propName INTEGER(".$this->BO->getPropObject($propName)->getSize().') NOT NULL,';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1278
                        } else {
1279
                            $sqlQuery .= "$propName INTEGER(".$this->BO->getPropObject($propName)->getSize().'),';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1280
                        }
1281
                    break;
1282
                    case 'DOUBLE':
1283
                        $sqlQuery .= "$propName REAL(".$this->BO->getPropObject($propName)->getSize(true).'),';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1284
                    break;
1285
                    case 'SMALLTEXT':
1286
                        $sqlQuery .= "$propName TEXT(".$this->BO->getPropObject($propName)->getSize().'),';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1287
                    break;
1288
                    case 'TEXT':
1289
                        $sqlQuery .= "$propName TEXT,";
1290
                    break;
1291
                    case 'BOOLEAN':
1292
                        $sqlQuery .= "$propName INTEGER(1) DEFAULT '0',";
1293
                    break;
1294
                    case 'DATE':
1295
                        $sqlQuery .= "$propName TEXT,";
1296
                    break;
1297
                    case 'TIMESTAMP':
1298
                        $sqlQuery .= "$propName TEXT,";
1299
                    break;
1300
                    case 'ENUM':
1301
                        $sqlQuery .= "$propName TEXT,";
1302
                    break;
1303
                    case 'DENUM':
1304
                        $tmp = new DEnum(get_class($this->BO).'::'.$propName);
0 ignored issues
show
Documentation introduced by
get_class($this->BO) . '::' . $propName is of type string, but the function expects a object<Alpha\Model\Type\SmallText>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Unused Code introduced by
$tmp 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...
1305
                        $sqlQuery .= "$propName INTEGER(11),";
1306
                    break;
1307
                    case 'RELATION':
1308
                        $sqlQuery .= "$propName INTEGER(11),";
1309
1310
                        $rel = $this->BO->getPropObject($propName);
1311
1312
                        $relatedField = $rel->getRelatedClassField();
1313
                        $relatedClass = $rel->getRelatedClass();
1314
                        $relatedBO = new $relatedClass();
1315
                        $tableName = $relatedBO->getTableName();
1316
                        $foreignKeys[$propName] = array($tableName, $relatedField);
1317
                    break;
1318
                    default:
1319
                        $sqlQuery .= '';
1320
                    break;
1321
                }
1322
            }
1323
        }
1324
        if ($this->BO->isTableOverloaded()) {
1325
            $sqlQuery .= 'classname TEXT(100)';
1326
        } else {
1327
            $sqlQuery = mb_substr($sqlQuery, 0, -1);
1328
        }
1329
1330
        if (count($foreignKeys) > 0) {
1331 View Code Duplication
            foreach ($foreignKeys as $field => $related) {
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...
1332
                $sqlQuery .= ', FOREIGN KEY ('.$field.') REFERENCES '.$related[0].'('.$related[1].')';
1333
            }
1334
        }
1335
1336
        if (count($this->foreignKeys) > 0) {
1337 View Code Duplication
            foreach ($this->foreignKeys as $field => $related) {
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...
1338
                $sqlQuery .= ', FOREIGN KEY ('.$field.') REFERENCES '.$related[0].'('.$related[1].')';
1339
            }
1340
        }
1341
1342
        $sqlQuery .= ');';
1343
1344
        $this->BO->setLastQuery($sqlQuery);
1345
1346
        if (!self::getConnection()->exec($sqlQuery)) {
1347
            self::$logger->debug('<<makeTable');
1348
            throw new AlphaException('Failed to create the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.self::getLastDatabaseError().']');
1349
        }
1350
1351
        // check the table indexes if any additional ones required
1352
        $this->checkIndexes();
1353
1354
        if ($this->BO->getMaintainHistory()) {
1355
            $this->BO->makeHistoryTable();
1356
        }
1357
1358
        self::$logger->debug('<<makeTable');
1359
    }
1360
1361
    /**
1362
     * (non-PHPdoc).
1363
     *
1364
     * @see Alpha\Model\ActiveRecordProviderInterface::makeHistoryTable()
1365
     */
1366
    public function makeHistoryTable()
1367
    {
1368
        self::$logger->debug('>>makeHistoryTable()');
1369
1370
        $sqlQuery = 'CREATE TABLE '.$this->BO->getTableName().'_history (OID INTEGER NOT NULL,';
1371
1372
        // get the class attributes
1373
        $reflection = new ReflectionClass(get_class($this->BO));
1374
        $properties = $reflection->getProperties();
1375
1376
        foreach ($properties as $propObj) {
1377
            $propName = $propObj->name;
1378
1379 View Code Duplication
            if (!in_array($propName, $this->BO->getTransientAttributes()) && $propName != 'OID') {
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...
1380
                $propReflect = new ReflectionClass($this->BO->getPropObject($propName));
1381
                $propClass = $propReflect->getShortName();
1382
1383
                switch (mb_strtoupper($propClass)) {
1384
                    case 'INTEGER':
1385
                        // special properties for RelationLookup OIDs
1386
                        if ($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID')) {
1387
                            $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().') NOT NULL,';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1388
                        } else {
1389
                            $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().'),';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1390
                        }
1391
                    break;
1392
                    case 'DOUBLE':
1393
                        $sqlQuery .= "$propName REAL(".$this->BO->getPropObject($propName)->getSize(true).'),';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1394
                    break;
1395
                    case 'SMALLTEXT':
1396
                        $sqlQuery .= "$propName TEXT(".$this->BO->getPropObject($propName)->getSize().'),';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1397
                    break;
1398
                    case 'TEXT':
1399
                        $sqlQuery .= "$propName TEXT,";
1400
                    break;
1401
                    case 'BOOLEAN':
1402
                        $sqlQuery .= "$propName INTEGER(1) DEFAULT '0',";
1403
                    break;
1404
                    case 'DATE':
1405
                        $sqlQuery .= "$propName TEXT,";
1406
                    break;
1407
                    case 'TIMESTAMP':
1408
                        $sqlQuery .= "$propName TEXT,";
1409
                    break;
1410
                    case 'ENUM':
1411
                        $sqlQuery .= "$propName TEXT,";
1412
                    break;
1413
                    case 'DENUM':
1414
                        $tmp = new DEnum(get_class($this->BO).'::'.$propName);
0 ignored issues
show
Documentation introduced by
get_class($this->BO) . '::' . $propName is of type string, but the function expects a object<Alpha\Model\Type\SmallText>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Unused Code introduced by
$tmp 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...
1415
                        $sqlQuery .= "$propName INTEGER(11),";
1416
                    break;
1417
                    case 'RELATION':
1418
                        $sqlQuery .= "$propName INTEGER(11),";
1419
                    break;
1420
                    default:
1421
                        $sqlQuery .= '';
1422
                    break;
1423
                }
1424
            }
1425
        }
1426
1427
        if ($this->BO->isTableOverloaded()) {
1428
            $sqlQuery .= 'classname TEXT(100),';
1429
        }
1430
1431
        $sqlQuery .= 'PRIMARY KEY (OID, version_num));';
1432
1433
        $this->BO->setLastQuery($sqlQuery);
1434
1435
        if (!$result = self::getConnection()->query($sqlQuery)) {
1436
            self::$logger->debug('<<makeHistoryTable');
1437
            throw new AlphaException('Failed to create the table ['.$this->BO->getTableName().'_history] for the class ['.get_class($this->BO).'], database error is ['.self::getLastDatabaseError().']');
1438
        }
1439
1440
        self::$logger->debug('<<makeHistoryTable');
1441
    }
1442
1443
    /**
1444
     * (non-PHPdoc).
1445
     *
1446
     * @see Alpha\Model\ActiveRecordProviderInterface::rebuildTable()
1447
     */
1448 View Code Duplication
    public function rebuildTable()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1449
    {
1450
        self::$logger->debug('>>rebuildTable()');
1451
1452
        // the use of "IF EXISTS" here requires SQLite 3.3.0 or above.
1453
        $sqlQuery = 'DROP TABLE IF EXISTS '.$this->BO->getTableName().';';
1454
1455
        $this->BO->setLastQuery($sqlQuery);
1456
1457
        if (!$result = self::getConnection()->query($sqlQuery)) {
1458
            self::$logger->debug('<<rebuildTable');
1459
            throw new AlphaException('Failed to drop the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.self::getLastDatabaseError().']');
1460
        }
1461
1462
        $this->BO->makeTable();
1463
1464
        self::$logger->debug('<<rebuildTable');
1465
    }
1466
1467
    /**
1468
     * (non-PHPdoc).
1469
     *
1470
     * @see Alpha\Model\ActiveRecordProviderInterface::dropTable()
1471
     */
1472 View Code Duplication
    public function dropTable($tableName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1473
    {
1474
        self::$logger->debug('>>dropTable()');
1475
1476
        if ($tableName == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $tableName of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1477
            $tableName = $this->BO->getTableName();
1478
        }
1479
1480
        // the use of "IF EXISTS" here requires SQLite 3.3.0 or above.
1481
        $sqlQuery = 'DROP TABLE IF EXISTS '.$tableName.';';
1482
1483
        $this->BO->setLastQuery($sqlQuery);
1484
1485
        if (!$result = self::getConnection()->query($sqlQuery)) {
1486
            self::$logger->debug('<<dropTable');
1487
            throw new AlphaException('Failed to drop the table ['.$tableName.'] for the class ['.get_class($this->BO).'], query is ['.$this->BO->getLastQuery().']');
1488
        }
1489
1490
        if ($this->BO->getMaintainHistory()) {
1491
            $sqlQuery = 'DROP TABLE IF EXISTS '.$tableName.'_history;';
1492
1493
            $this->BO->setLastQuery($sqlQuery);
1494
1495
            if (!$result = self::getConnection()->query($sqlQuery)) {
1496
                self::$logger->debug('<<dropTable');
1497
                throw new AlphaException('Failed to drop the table ['.$tableName.'_history] for the class ['.get_class($this->BO).'], query is ['.$this->BO->getLastQuery().']');
1498
            }
1499
        }
1500
1501
        self::$logger->debug('<<dropTable');
1502
    }
1503
1504
    /**
1505
     * (non-PHPdoc).
1506
     *
1507
     * @see Alpha\Model\ActiveRecordProviderInterface::addProperty()
1508
     */
1509
    public function addProperty($propName)
1510
    {
1511
        self::$logger->debug('>>addProperty(propName=['.$propName.'])');
1512
1513
        $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD ';
1514
1515
        if ($this->isTableOverloaded() && $propName == 'classname') {
1516
            $sqlQuery .= 'classname TEXT(100)';
1517 View Code Duplication
        } else {
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...
1518
            if (!in_array($propName, $this->BO->getDefaultAttributes()) && !in_array($propName, $this->BO->getTransientAttributes())) {
1519
                $reflection = new ReflectionClass($this->BO->getPropObject($propName));
1520
                $propClass = $reflection->getShortName();
1521
1522
                switch (mb_strtoupper($propClass)) {
1523
                    case 'INTEGER':
1524
                        // special properties for RelationLookup OIDs
1525
                        if ($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID')) {
1526
                            $sqlQuery .= "$propName INTEGER(".$this->BO->getPropObject($propName)->getSize().') NOT NULL,';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1527
                        } else {
1528
                            $sqlQuery .= "$propName INTEGER(".$this->BO->getPropObject($propName)->getSize().'),';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1529
                        }
1530
                    break;
1531
                    case 'DOUBLE':
1532
                        $sqlQuery .= "$propName REAL(".$this->BO->getPropObject($propName)->getSize(true).'),';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1533
                    break;
1534
                    case 'SMALLTEXT':
1535
                        $sqlQuery .= "$propName TEXT(".$this->BO->getPropObject($propName)->getSize().'),';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Alpha\Model\Type\TypeInterface as the method getSize() does only exist in the following implementations of said interface: Alpha\Model\Type\Double, Alpha\Model\Type\Integer, Alpha\Model\Type\Relation, Alpha\Model\Type\Sequence, Alpha\Model\Type\SmallText, Alpha\Model\Type\Text.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1536
                    break;
1537
                    case 'TEXT':
1538
                        $sqlQuery .= "$propName TEXT,";
1539
                    break;
1540
                    case 'BOOLEAN':
1541
                        $sqlQuery .= "$propName INTEGER(1) DEFAULT '0',";
1542
                    break;
1543
                    case 'DATE':
1544
                        $sqlQuery .= "$propName TEXT,";
1545
                    break;
1546
                    case 'TIMESTAMP':
1547
                        $sqlQuery .= "$propName TEXT,";
1548
                    break;
1549
                    case 'ENUM':
1550
                        $sqlQuery .= "$propName TEXT,";
1551
                    break;
1552
                    case 'DENUM':
1553
                        $tmp = new DEnum(get_class($this->BO).'::'.$propName);
0 ignored issues
show
Documentation introduced by
get_class($this->BO) . '::' . $propName is of type string, but the function expects a object<Alpha\Model\Type\SmallText>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Unused Code introduced by
$tmp 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...
1554
                        $sqlQuery .= "$propName INTEGER(11),";
1555
                    break;
1556
                    case 'RELATION':
1557
                        $sqlQuery .= "$propName INTEGER(11),";
1558
                    break;
1559
                    default:
1560
                        $sqlQuery .= '';
1561
                    break;
1562
                }
1563
            }
1564
        }
1565
1566
        $this->BO->setLastQuery($sqlQuery);
1567
1568
        if (!$result = self::getConnection()->query($sqlQuery)) {
1569
            self::$logger->debug('<<addProperty');
1570
            throw new AlphaException('Failed to add the new attribute ['.$propName.'] to the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1571
        } else {
1572
            self::$logger->info('Successfully added the ['.$propName.'] column onto the ['.$this->BO->getTableName().'] table for the class ['.get_class($this->BO).']');
1573
        }
1574
1575 View Code Duplication
        if ($this->BO->getMaintainHistory()) {
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...
1576
            $sqlQuery = str_replace($this->BO->getTableName(), $this->BO->getTableName().'_history', $sqlQuery);
1577
1578
            if (!$result = self::getConnection()->query($sqlQuery)) {
1579
                self::$logger->debug('<<addProperty');
1580
                throw new AlphaException('Failed to add the new attribute ['.$propName.'] to the table ['.$this->BO->getTableName().'_history], query is ['.$this->BO->getLastQuery().']');
1581
            } else {
1582
                self::$logger->info('Successfully added the ['.$propName.'] column onto the ['.$this->BO->getTableName().'_history] table for the class ['.get_class($this->BO).']');
1583
            }
1584
        }
1585
1586
        self::$logger->debug('<<addProperty');
1587
    }
1588
1589
    /**
1590
     * (non-PHPdoc).
1591
     *
1592
     * @see Alpha\Model\ActiveRecordProviderInterface::getMAX()
1593
     */
1594 View Code Duplication
    public function getMAX()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1595
    {
1596
        self::$logger->debug('>>getMAX()');
1597
1598
        $sqlQuery = 'SELECT MAX(OID) AS max_OID FROM '.$this->BO->getTableName();
1599
1600
        $this->BO->setLastQuery($sqlQuery);
1601
1602
        try {
1603
            $result = $this->BO->query($sqlQuery);
1604
1605
            $row = $result[0];
1606
1607
            if (isset($row['max_OID'])) {
1608
                self::$logger->debug('<<getMAX ['.$row['max_OID'].']');
1609
1610
                return $row['max_OID'];
1611
            } else {
1612
                throw new AlphaException('Failed to get the MAX ID for the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1613
            }
1614
        } catch (Exception $e) {
1615
            self::$logger->debug('<<getMAX');
1616
            throw new AlphaException($e->getMessage());
1617
        }
1618
    }
1619
1620
    /**
1621
     * (non-PHPdoc).
1622
     *
1623
     * @see Alpha\Model\ActiveRecordProviderInterface::getCount()
1624
     */
1625
    public function getCount($attributes = array(), $values = array())
1626
    {
1627
        self::$logger->debug('>>getCount(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'])');
1628
1629
        if ($this->BO->isTableOverloaded()) {
1630
            $whereClause = ' WHERE classname = \''.addslashes(get_class($this->BO)).'\' AND';
1631
        } else {
1632
            $whereClause = ' WHERE';
1633
        }
1634
1635
        $count = count($attributes);
1636
1637
        for ($i = 0; $i < $count; ++$i) {
1638
            $whereClause .= ' '.$attributes[$i].' = \''.$values[$i].'\' AND';
1639
            self::$logger->debug($whereClause);
1640
        }
1641
        // remove the last " AND"
1642
        $whereClause = mb_substr($whereClause, 0, -4);
1643
1644
        if ($whereClause != ' WHERE') {
1645
            $sqlQuery = 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName().$whereClause;
1646
        } else {
1647
            $sqlQuery = 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName();
1648
        }
1649
1650
        $this->BO->setLastQuery($sqlQuery);
1651
1652 View Code Duplication
        if (!$result = self::getConnection()->query($sqlQuery)) {
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...
1653
            self::$logger->debug('<<getCount');
1654
            throw new AlphaException('Failed to get the count for the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1655
        } else {
1656
            $row = $result->fetchArray(SQLITE3_ASSOC);
1657
1658
            self::$logger->debug('<<getCount ['.$row['class_count'].']');
1659
1660
            return $row['class_count'];
1661
        }
1662
    }
1663
1664
    /**
1665
     * (non-PHPdoc).
1666
     *
1667
     * @see Alpha\Model\ActiveRecordProviderInterface::getHistoryCount()
1668
     */
1669
    public function getHistoryCount()
1670
    {
1671
        self::$logger->debug('>>getHistoryCount()');
1672
1673
        if (!$this->BO->getMaintainHistory()) {
1674
            throw new AlphaException('getHistoryCount method called on a DAO where no history is maintained!');
1675
        }
1676
1677
        $sqlQuery = 'SELECT COUNT(OID) AS object_count FROM '.$this->BO->getTableName().'_history WHERE OID='.$this->BO->getOID();
1678
1679
        $this->BO->setLastQuery($sqlQuery);
1680
        self::$logger->debug('query ['.$sqlQuery.']');
1681
1682 View Code Duplication
        if (!$result = self::getConnection()->query($sqlQuery)) {
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...
1683
            self::$logger->debug('<<getHistoryCount');
1684
            throw new AlphaException('Failed to get the history count for the business object ['.$this->BO->getOID().'] from the table ['.$this->BO->getTableName().'_history], query is ['.$this->BO->getLastQuery().']');
1685
        } else {
1686
            $row = $result->fetchArray(SQLITE3_ASSOC);
1687
1688
            self::$logger->debug('<<getHistoryCount ['.$row['object_count'].']');
1689
1690
            return $row['object_count'];
1691
        }
1692
    }
1693
1694
    /**
1695
     * Given that Enum values are not saved in the database for SQLite, an implementation is not required here.
1696
     *
1697
     * (non-PHPdoc)
1698
     *
1699
     * @see Alpha\Model\ActiveRecordProviderInterface::setEnumOptions()
1700
     *
1701
     * @throws \Alpha\Exception\NotImplementedException
1702
     */
1703
    public function setEnumOptions()
1704
    {
1705
        throw new NotImplementedException('ActiveRecordProviderInterface::setEnumOptions() not implemented by the SQLite3 provider');
1706
    }
1707
1708
    /**
1709
     * (non-PHPdoc).
1710
     *
1711
     * @see Alpha\Model\ActiveRecordProviderInterface::checkTableExists()
1712
     */
1713 View Code Duplication
    public function checkTableExists($checkHistoryTable = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1714
    {
1715
        self::$logger->debug('>>checkTableExists(checkHistoryTable=['.$checkHistoryTable.'])');
1716
1717
        $config = ConfigProvider::getInstance();
0 ignored issues
show
Unused Code introduced by
$config 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...
1718
1719
        $tableExists = false;
1720
1721
        $sqlQuery = 'SELECT name FROM sqlite_master WHERE type = "table";';
1722
        $this->BO->setLastQuery($sqlQuery);
1723
1724
        $result = self::getConnection()->query($sqlQuery);
1725
1726
        $tableName = ($checkHistoryTable ? $this->BO->getTableName().'_history' : $this->BO->getTableName());
1727
1728
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1729
            if (strtolower($row['name']) == mb_strtolower($tableName)) {
1730
                $tableExists = true;
1731
            }
1732
        }
1733
1734
        if ($result) {
1735
            self::$logger->debug('<<checkTableExists ['.$tableExists.']');
1736
1737
            return $tableExists;
1738
        } else {
1739
            self::$logger->debug('<<checkTableExists');
1740
            throw new AlphaException('Failed to access the system database correctly, error is ['.self::getLastDatabaseError().']');
1741
        }
1742
    }
1743
1744
    /**
1745
     * (non-PHPdoc).
1746
     *
1747
     * @see Alpha\Model\ActiveRecordProviderInterface::checkBOTableExists()
1748
     */
1749 View Code Duplication
    public static function checkBOTableExists($BOClassName, $checkHistoryTable = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1750
    {
1751
        if (self::$logger == null) {
1752
            self::$logger = new Logger('ActiveRecordProviderSQLite');
1753
        }
1754
        self::$logger->debug('>>checkBOTableExists(BOClassName=['.$BOClassName.'], checkHistoryTable=['.$checkHistoryTable.'])');
1755
1756
        if (!class_exists($BOClassName)) {
1757
            throw new IllegalArguementException('The classname provided ['.$checkHistoryTable.'] is not defined!');
1758
        }
1759
1760
        $tableName = $BOClassName::TABLE_NAME;
1761
1762
        if (empty($tableName)) {
1763
            $tableName = mb_substr($BOClassName, 0, mb_strpos($BOClassName, '_'));
1764
        }
1765
1766
        if ($checkHistoryTable) {
1767
            $tableName .= '_history';
1768
        }
1769
1770
        $tableExists = false;
1771
1772
        $sqlQuery = 'SELECT name FROM sqlite_master WHERE type = "table";';
1773
1774
        $result = self::getConnection()->query($sqlQuery);
1775
1776
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1777
            if ($row['name'] == $tableName) {
1778
                $tableExists = true;
1779
            }
1780
        }
1781
1782
        if ($result) {
1783
            self::$logger->debug('<<checkBOTableExists ['.($tableExists ? 'true' : 'false').']');
1784
1785
            return $tableExists;
1786
        } else {
1787
            self::$logger->debug('<<checkBOTableExists');
1788
            throw new AlphaException('Failed to access the system database correctly, error is ['.self::getLastDatabaseError().']');
1789
        }
1790
    }
1791
1792
    /**
1793
     * (non-PHPdoc).
1794
     *
1795
     * @see Alpha\Model\ActiveRecordProviderInterface::checkTableNeedsUpdate()
1796
     */
1797 View Code Duplication
    public function checkTableNeedsUpdate()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1798
    {
1799
        self::$logger->debug('>>checkTableNeedsUpdate()');
1800
1801
        if (!$this->BO->checkTableExists()) {
1802
            return false;
1803
        }
1804
1805
        $updateRequired = false;
1806
1807
        $matchCount = 0;
1808
1809
        $query = 'PRAGMA table_info('.$this->BO->getTableName().')';
1810
        $result = self::getConnection()->query($query);
1811
        $this->BO->setLastQuery($query);
1812
1813
        // get the class attributes
1814
        $reflection = new ReflectionClass(get_class($this->BO));
1815
        $properties = $reflection->getProperties();
1816
1817
        foreach ($properties as $propObj) {
1818
            $propName = $propObj->name;
1819
            if (!in_array($propName, $this->BO->getTransientAttributes())) {
1820
                $foundMatch = false;
1821
1822
                while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1823
                    if ($propName == $row['name']) {
1824
                        $foundMatch = true;
1825
                        break;
1826
                    }
1827
                }
1828
1829
                if (!$foundMatch) {
1830
                    --$matchCount;
1831
                }
1832
1833
                $result->reset();
1834
            }
1835
        }
1836
1837
        // check for the "classname" field in overloaded tables
1838
        if ($this->BO->isTableOverloaded()) {
1839
            $foundMatch = false;
1840
1841
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1842
                if ('classname' == $row['name']) {
1843
                    $foundMatch = true;
1844
                    break;
1845
                }
1846
            }
1847
            if (!$foundMatch) {
1848
                --$matchCount;
1849
            }
1850
        }
1851
1852
        if ($matchCount != 0) {
1853
            $updateRequired = true;
1854
        }
1855
1856
        if (!$result) {
1857
            self::$logger->debug('<<checkTableNeedsUpdate');
1858
            throw new AlphaException('Failed to access the system database correctly, error is ['.self::getLastDatabaseError().']');
1859
        } else {
1860
            // check the table indexes
1861
            try {
1862
                $this->checkIndexes();
1863
            } catch (AlphaException $ae) {
1864
                self::$logger->warn("Error while checking database indexes:\n\n".$ae->getMessage());
1865
            }
1866
1867
            self::$logger->debug('<<checkTableNeedsUpdate ['.$updateRequired.']');
1868
1869
            return $updateRequired;
1870
        }
1871
    }
1872
1873
    /**
1874
     * (non-PHPdoc).
1875
     *
1876
     * @see Alpha\Model\ActiveRecordProviderInterface::findMissingFields()
1877
     */
1878 View Code Duplication
    public function findMissingFields()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1879
    {
1880
        self::$logger->debug('>>findMissingFields()');
1881
1882
        $missingFields = array();
1883
        $matchCount = 0;
1884
1885
        $sqlQuery = 'PRAGMA table_info('.$this->BO->getTableName().')';
1886
        $result = self::getConnection()->query($sqlQuery);
1887
        $this->BO->setLastQuery($sqlQuery);
1888
1889
        // get the class attributes
1890
        $reflection = new ReflectionClass(get_class($this->BO));
1891
        $properties = $reflection->getProperties();
1892
1893
        foreach ($properties as $propObj) {
1894
            $propName = $propObj->name;
1895
            if (!in_array($propName, $this->BO->getTransientAttributes())) {
1896
                while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1897
                    if ($propName == $row['name']) {
1898
                        ++$matchCount;
1899
                        break;
1900
                    }
1901
                }
1902
                $result->reset();
1903
            } else {
1904
                ++$matchCount;
1905
            }
1906
1907
            if ($matchCount == 0) {
1908
                array_push($missingFields, $propName);
1909
            } else {
1910
                $matchCount = 0;
1911
            }
1912
        }
1913
1914
        // check for the "classname" field in overloaded tables
1915
        if ($this->BO->isTableOverloaded()) {
1916
            $foundMatch = false;
1917
1918
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1919
                if ('classname' == $row['name']) {
1920
                    $foundMatch = true;
1921
                    break;
1922
                }
1923
            }
1924
            if (!$foundMatch) {
1925
                array_push($missingFields, 'classname');
1926
            }
1927
        }
1928
1929
        if (!$result) {
1930
            throw new AlphaException('Failed to access the system database correctly, error is ['.self::getLastDatabaseError().']');
1931
        }
1932
1933
        self::$logger->debug('<<findMissingFields ['.var_export($missingFields, true).']');
1934
1935
        return $missingFields;
1936
    }
1937
1938
    /**
1939
     * (non-PHPdoc).
1940
     *
1941
     * @see Alpha\Model\ActiveRecordProviderInterface::getIndexes()
1942
     */
1943
    public function getIndexes()
1944
    {
1945
        self::$logger->debug('>>getIndexes()');
1946
1947
        $sqlQuery = "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='".$this->BO->getTableName()."'";
1948
1949
        $this->BO->setLastQuery($sqlQuery);
1950
1951
        $indexNames = array();
1952
1953
        if (!$result = self::getConnection()->query($sqlQuery)) {
1954
            throw new AlphaException('Failed to access the system database correctly, error is ['.self::getLastDatabaseError().']');
1955
        } else {
1956
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1957
                array_push($indexNames, $row['name']);
1958
            }
1959
        }
1960
1961
        // in SQLite foreign keys are not stored in sqlite_master, so we have to run a different query and append the results
1962
        $sqlQuery = 'PRAGMA foreign_key_list('.$this->BO->getTableName().')';
1963
        
1964
        $this->BO->setLastQuery($sqlQuery);
1965
1966
        if (!$result = self::getConnection()->query($sqlQuery)) {
1967
            self::$logger->warn('Error during pragma table foreign key lookup ['.self::getLastDatabaseError().']');
1968
        } else {
1969
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1970
                // SQLite does not name FK indexes, so we will return a fake name based the same convention used in MySQL
1971
                $fakeIndexName = $this->BO->getTableName().'_'.$row['from'].'_fk_idx';
1972
                array_push($indexNames, $fakeIndexName);
1973
            }
1974
        }
1975
1976
        self::$logger->debug('<<getIndexes');
1977
1978
        return $indexNames;
1979
    }
1980
1981
    /**
1982
     * Checks to see if all of the indexes are in place for the BO's table, creates those that are missing.
1983
     *
1984
     * @since 1.2
1985
     */
1986
    private function checkIndexes()
1987
    {
1988
        self::$logger->debug('>>checkIndexes()');
1989
1990
        $indexNames = $this->BO->getIndexes();
1991
1992
        // process unique keys
1993 View Code Duplication
        foreach ($this->BO->getUniqueAttributes() as $prop) {
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...
1994
            // check for composite indexes
1995
            if (mb_strpos($prop, '+')) {
1996
                $attributes = explode('+', $prop);
1997
1998
                $index_exists = false;
1999
                foreach ($indexNames as $index) {
2000
                    if ($attributes[0].'_'.$attributes[1].'_unq_idx' == $index) {
2001
                        $index_exists = true;
2002
                    }
2003
                    if (count($attributes) == 3) {
2004
                        if ($attributes[0].'_'.$attributes[1].'_'.$attributes[2].'_unq_idx' == $index) {
2005
                            $index_exists = true;
2006
                        }
2007
                    }
2008
                }
2009
2010
                if (!$index_exists) {
2011
                    if (count($attributes) == 3) {
2012
                        $this->BO->createUniqueIndex($attributes[0], $attributes[1], $attributes[2]);
2013
                    } else {
2014
                        $this->BO->createUniqueIndex($attributes[0], $attributes[1]);
2015
                    }
2016
                }
2017
            } else {
2018
                $index_exists = false;
2019
                foreach ($indexNames as $index) {
2020
                    if ($prop.'_unq_idx' == $index) {
2021
                        $index_exists = true;
2022
                    }
2023
                }
2024
2025
                if (!$index_exists) {
2026
                    $this->createUniqueIndex($prop);
2027
                }
2028
            }
2029
        }
2030
2031
        self::$logger->debug('<<checkIndexes');
2032
    }
2033
2034
    /**
2035
     * Note that SQLite 3.6.19 is requrired for foreign key support.
2036
     *
2037
     * (non-PHPdoc)
2038
     *
2039
     * @see Alpha\Model\ActiveRecordProviderInterface::createForeignIndex()
2040
     */
2041
    public function createForeignIndex($attributeName, $relatedClass, $relatedClassAttribute, $indexName = null)
2042
    {
2043
        self::$logger->info('>>createForeignIndex(attributeName=['.$attributeName.'], relatedClass=['.$relatedClass.'], relatedClassAttribute=['.$relatedClassAttribute.'], indexName=['.$indexName.']');
2044
2045
        /*
2046
         * High-level approach
2047
         *
2048
         * 1. Rename the source table to [tablename]_temp
2049
         * 2. Create a new [tablename] table, with the new FK in place.
2050
         * 3. Copy all of the data from [tablename]_temp to [tablename].
2051
         * 4. Drop [tablename]_temp.
2052
         */
2053
        try {
2054
            ActiveRecord::begin($this->BO);
2055
2056
            // rename the table to [tablename]_temp
2057
            $query = 'ALTER TABLE '.$this->BO->getTableName().' RENAME TO '.$this->BO->getTableName().'_temp;';
2058
            $this->BO->setLastQuery($query);
2059
            self::getConnection()->query($query);
2060
2061
            self::$logger->info('Renamed the table ['.$this->BO->getTableName().'] to ['.$this->BO->getTableName().'_temp]');
2062
2063
            // now create the new table with the FK in place
2064
            $record = new $relatedClass();
2065
            $tableName = $record->getTableName();
2066
            $this->foreignKeys[$attributeName] = array($tableName, $relatedClassAttribute);
2067
2068
            $this->makeTable();
2069
2070
            self::$logger->info('Made a new copy of the table ['.$this->BO->getTableName().']');
2071
2072
            // copy all of the old data to the new table
2073
            $query = 'INSERT INTO '.$this->BO->getTableName().' SELECT * FROM '.$this->BO->getTableName().'_temp;';
2074
            $this->BO->setLastQuery($query);
2075
            self::getConnection()->query($query);
2076
2077
            self::$logger->info('Copied all of the data from ['.$this->BO->getTableName().'] to ['.$this->BO->getTableName().'_temp]');
2078
2079
            // finally, drop the _temp table and commit the changes
2080
            $this->BO->dropTable($this->BO->getTableName().'_temp');
2081
2082
            self::$logger->info('Dropped the table ['.$this->BO->getTableName().'_temp]');
2083
2084
            ActiveRecord::commit($this->BO);
2085
        } catch (Exception $e) {
2086
            ActiveRecord::rollback($this->BO);
2087
2088
            throw new FailedIndexCreateException('Failed to create the index ['.$attributeName.'] on ['.$this->BO->getTableName().'], error is ['.$e->getMessage().'], query ['.$this->BO->getLastQuery().']');
2089
        }
2090
2091
        self::$logger->info('<<createForeignIndex');
2092
    }
2093
2094
    /**
2095
     * (non-PHPdoc).
2096
     *
2097
     * @see Alpha\Model\ActiveRecordProviderInterface::createUniqueIndex()
2098
     */
2099 View Code Duplication
    public function createUniqueIndex($attribute1Name, $attribute2Name = '', $attribute3Name = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
2100
    {
2101
        self::$logger->debug('>>createUniqueIndex(attribute1Name=['.$attribute1Name.'], attribute2Name=['.$attribute2Name.'], attribute3Name=['.$attribute3Name.'])');
2102
2103
        if ($attribute2Name != '' && $attribute3Name != '') {
2104
            $sqlQuery = 'CREATE UNIQUE INDEX IF NOT EXISTS '.$attribute1Name.'_'.$attribute2Name.'_'.$attribute3Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.','.$attribute3Name.');';
2105
        }
2106
2107
        if ($attribute2Name != '' && $attribute3Name == '') {
2108
            $sqlQuery = 'CREATE UNIQUE INDEX IF NOT EXISTS '.$attribute1Name.'_'.$attribute2Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.');';
2109
        }
2110
2111
        if ($attribute2Name == '' && $attribute3Name == '') {
2112
            $sqlQuery = 'CREATE UNIQUE INDEX IF NOT EXISTS '.$attribute1Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.');';
2113
        }
2114
2115
        $this->BO->setLastQuery($sqlQuery);
0 ignored issues
show
Bug introduced by
The variable $sqlQuery does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2116
2117
        $result = self::getConnection()->query($sqlQuery);
2118
2119
        if ($result) {
2120
            self::$logger->debug('Successfully created the unique index on ['.$this->BO->getTableName().']');
2121
        } else {
2122
            throw new FailedIndexCreateException('Failed to create the unique index on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.']');
0 ignored issues
show
Bug introduced by
The property error does not seem to exist in SQLite3.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
2123
        }
2124
2125
        self::$logger->debug('<<createUniqueIndex');
2126
    }
2127
2128
    /**
2129
     * (non-PHPdoc).
2130
     *
2131
     * @see Alpha\Model\ActiveRecordProviderInterface::reload()
2132
     */
2133 View Code Duplication
    public function reload()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
2134
    {
2135
        self::$logger->debug('>>reload()');
2136
2137
        if (!$this->BO->isTransient()) {
2138
            $this->BO->load($this->BO->getOID());
2139
        } else {
2140
            throw new AlphaException('Cannot reload transient object from database!');
2141
        }
2142
2143
        self::$logger->debug('<<reload');
2144
    }
2145
2146
    /**
2147
     * (non-PHPdoc).
2148
     *
2149
     * @see Alpha\Model\ActiveRecordProviderInterface::checkRecordExists()
2150
     */
2151
    public function checkRecordExists($OID)
2152
    {
2153
        self::$logger->debug('>>checkRecordExists(OID=['.$OID.'])');
2154
2155
        $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE OID = :OID;';
2156
        $this->BO->setLastQuery($sqlQuery);
2157
        $stmt = self::getConnection()->prepare($sqlQuery);
2158
2159
        $row = array();
0 ignored issues
show
Unused Code introduced by
$row 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...
2160
2161
        if ($stmt instanceof SQLite3Stmt) {
2162
            $stmt->bindValue(':OID', $OID, SQLITE3_INTEGER);
2163
2164
            $result = $stmt->execute();
2165
2166
            // there should only ever be one (or none)
2167
            $row = $result->fetchArray(SQLITE3_ASSOC);
2168
2169
            $stmt->close();
2170
        } else {
2171
            self::$logger->debug('<<checkRecordExists');
2172
            throw new AlphaException('Failed to check for the record ['.$OID.'] on the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
2173
        }
2174
2175 View Code Duplication
        if (!isset($row['OID'])) {
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...
2176
            self::$logger->debug('<<checkRecordExists [false]');
2177
2178
            return false;
2179
        } else {
2180
            self::$logger->debug('<<checkRecordExists [true]');
2181
2182
            return true;
2183
        }
2184
    }
2185
2186
    /**
2187
     * (non-PHPdoc).
2188
     *
2189
     * @see Alpha\Model\ActiveRecordProviderInterface::isTableOverloaded()
2190
     */
2191 View Code Duplication
    public function isTableOverloaded()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
2192
    {
2193
        self::$logger->debug('>>isTableOverloaded()');
2194
2195
        $reflection = new ReflectionClass($this->BO);
2196
        $classname = $reflection->getShortName();
2197
        $tablename = ucfirst($this->BO->getTableName());
2198
2199
        // use reflection to check to see if we are dealing with a persistent type (e.g. DEnum) which are never overloaded
2200
        $implementedInterfaces = $reflection->getInterfaces();
2201
2202
        foreach ($implementedInterfaces as $interface) {
2203
            if ($interface->name == 'Alpha\Model\Type\TypeInterface') {
2204
                self::$logger->debug('<<isTableOverloaded [false]');
2205
2206
                return false;
2207
            }
2208
        }
2209
2210
        if ($classname != $tablename) {
2211
            // loop over all BOs to see if there is one using the same table as this BO
2212
2213
            $BOclasses = ActiveRecord::getBOClassNames();
2214
2215
            foreach ($BOclasses as $BOclassName) {
2216
                $reflection = new ReflectionClass($BOclassName);
2217
                $classname = $reflection->getShortName();
2218
                if ($tablename == $classname) {
2219
                    self::$logger->debug('<<isTableOverloaded [true]');
2220
2221
                    return true;
2222
                }
2223
            }
2224
            self::$logger->debug('<<isTableOverloaded');
2225
            throw new BadTableNameException('The table name ['.$tablename.'] for the class ['.$classname.'] is invalid as it does not match a BO definition in the system!');
2226
        } else {
2227
            // check to see if there is already a "classname" column in the database for this BO
2228
            $sqlQuery = 'PRAGMA table_info('.$this->BO->getTableName().')';
2229
            $result = self::getConnection()->query($sqlQuery);
2230
            $this->BO->setLastQuery($sqlQuery);
2231
2232
            if (!$result) {
2233
                self::$logger->warn('Error during pragma table info lookup ['.self::getLastDatabaseError().']');
2234
            } else {
2235
                while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
2236
                    if ('classname' == $row['name']) {
2237
                        self::$logger->debug('<<isTableOverloaded [true]');
2238
2239
                        return true;
2240
                    }
2241
                }
2242
            }
2243
2244
            self::$logger->debug('<<isTableOverloaded [false]');
2245
2246
            return false;
2247
        }
2248
    }
2249
2250
    /**
2251
     * (non-PHPdoc).
2252
     *
2253
     * @see Alpha\Model\ActiveRecordProviderInterface::begin()
2254
     */
2255 View Code Duplication
    public static function begin()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
2256
    {
2257
        if (self::$logger == null) {
2258
            self::$logger = new Logger('ActiveRecordProviderSQLite');
2259
        }
2260
        self::$logger->debug('>>begin()');
2261
2262
        if (!self::getConnection()->exec('BEGIN')) {
2263
            throw new AlphaException('Error beginning a new transaction, error is ['.self::getLastDatabaseError().']');
2264
        }
2265
2266
        self::$logger->debug('<<begin');
2267
    }
2268
2269
    /**
2270
     * (non-PHPdoc).
2271
     *
2272
     * @see Alpha\Model\ActiveRecordProviderInterface::commit()
2273
     */
2274 View Code Duplication
    public static function commit()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
2275
    {
2276
        if (self::$logger == null) {
2277
            self::$logger = new Logger('ActiveRecordProviderSQLite');
2278
        }
2279
        self::$logger->debug('>>commit()');
2280
2281
        if (!self::getConnection()->exec('COMMIT')) {
2282
            throw new AlphaException('Error commiting a transaction, error is ['.self::getLastDatabaseError().']');
2283
        }
2284
2285
        self::$logger->debug('<<commit');
2286
    }
2287
2288
    /**
2289
     * (non-PHPdoc).
2290
     *
2291
     * @see Alpha\Model\ActiveRecordProviderInterface::rollback()
2292
     */
2293
    public static function rollback()
2294
    {
2295
        if (self::$logger == null) {
2296
            self::$logger = new Logger('ActiveRecordProviderSQLite');
2297
        }
2298
2299
        self::$logger->debug('>>rollback()');
2300
2301
        try {
2302
            self::getConnection()->exec('ROLLBACK');
2303
            self::disconnect();
2304
        } catch (Exception $e) {
2305
            if (mb_strpos($e->getMessage(), 'cannot rollback - no transaction is active') === false) { // just filtering out errors where the rollback failed due to no current transaction
2306
                throw new AlphaException('Error rolling back a transaction, error is ['.self::getLastDatabaseError().']');
2307
            }
2308
        }
2309
2310
        self::$logger->debug('<<rollback');
2311
    }
2312
2313
    /**
2314
     * (non-PHPdoc).
2315
     *
2316
     * @see Alpha\Model\ActiveRecordProviderInterface::setBO()
2317
     */
2318
    public function setBO($BO)
2319
    {
2320
        $this->BO = $BO;
2321
    }
2322
2323
    /**
2324
     * (non-PHPdoc).
2325
     *
2326
     * @see Alpha\Model\ActiveRecordProviderInterface::checkDatabaseExists()
2327
     */
2328
    public static function checkDatabaseExists()
2329
    {
2330
        $config = ConfigProvider::getInstance();
2331
2332
        return file_exists($config->get('db.file.path'));
2333
    }
2334
2335
    /**
2336
     * (non-PHPdoc).
2337
     *
2338
     * @see Alpha\Model\ActiveRecordProviderInterface::createDatabase()
2339
     */
2340
    public static function createDatabase()
2341
    {
2342
        $config = ConfigProvider::getInstance();
2343
2344
        if (!self::checkDatabaseExists()) {
2345
            fopen($config->get('db.file.path'), 'x+');
2346
        }
2347
    }
2348
2349
    /**
2350
     * (non-PHPdoc).
2351
     *
2352
     * @see Alpha\Model\ActiveRecordProviderInterface::dropDatabase()
2353
     */
2354
    public static function dropDatabase()
2355
    {
2356
        $config = ConfigProvider::getInstance();
2357
2358
        if (self::checkDatabaseExists()) {
2359
            unlink($config->get('db.file.path'));
2360
        }
2361
    }
2362
}
2363