Completed
Pull Request — develop (#282)
by John
04:22
created

ActiveRecordProviderSQLite::getIndexes()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 37
rs 8.439
cc 5
eloc 20
nc 5
nop 0
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) 2015, 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');
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Alpha\Util\Logging\...eRecordProviderSQLite') of type object<Alpha\Util\Logging\Logger> is incompatible with the declared type object<Alpha\Model\Alpha\Util\Logging\Logger> of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
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 View Code Duplication
    public function query($sqlQuery)
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...
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
179
            return array();
0 ignored issues
show
Unused Code introduced by
return array(); 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...
180
        } else {
181
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
182
                array_push($resultArray, $row);
183
            }
184
185
            return $resultArray;
186
        }
187
    }
188
189
    /**
190
     * (non-PHPdoc).
191
     *
192
     * @see Alpha\Model\ActiveRecordProviderInterface::load()
193
     */
194
    public function load($OID, $version = 0)
195
    {
196
        self::$logger->debug('>>load(OID=['.$OID.'], version=['.$version.'])');
197
198
        $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...
199
200
        $attributes = $this->BO->getPersistentAttributes();
201
        $fields = '';
202
        foreach ($attributes as $att) {
203
            $fields .= $att.',';
204
        }
205
        $fields = mb_substr($fields, 0, -1);
206
207 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...
208
            $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().'_history WHERE OID = :OID AND version_num = :version LIMIT 1;';
209
        } else {
210
            $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE OID = :OID LIMIT 1;';
211
        }
212
        $this->BO->setLastQuery($sqlQuery);
213
214
        try {
215
            $stmt = self::getConnection()->prepare($sqlQuery);
216
217
            $row = array();
218
219
            if ($version > 0) {
220
                $stmt->bindValue(':version', $version, SQLITE3_INTEGER);
221
            }
222
223
            $stmt->bindValue(':OID', $OID, SQLITE3_INTEGER);
224
225
            $result = $stmt->execute();
226
227
            // there should only ever be one (or none)
228
            $row = $result->fetchArray(SQLITE3_ASSOC);
229
230
            $stmt->close();
231
        } catch (PHPException $e) {
232
            self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
233 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...
234
                $this->BO->makeTable();
235
236
                throw new RecordNotFoundException('Failed to load object of OID ['.$OID.'], table ['.$this->BO->getTableName().'] did not exist so had to create!');
237
            }
238
239
            return;
240
        }
241
242 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...
243
            throw new RecordNotFoundException('Failed to load object of OID ['.$OID.'] not found in database.');
244
            self::$logger->debug('<<load');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<load'); 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...
245
246
            return;
247
        }
248
249
        // get the class attributes
250
        $reflection = new ReflectionClass(get_class($this->BO));
251
        $properties = $reflection->getProperties();
252
253
        try {
254 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...
255
                $propName = $propObj->name;
256
257
                // filter transient attributes
258
                if (!in_array($propName, $this->BO->getTransientAttributes())) {
259
                    $this->BO->set($propName, $row[$propName]);
260
                } elseif (!$propObj->isPrivate() && $this->BO->getPropObject($propName) instanceof Relation) {
261
                    $prop = $this->BO->getPropObject($propName);
262
263
                    // handle the setting of ONE-TO-MANY relation values
264
                    if ($prop->getRelationType() == 'ONE-TO-MANY') {
265
                        $this->BO->set($propObj->name, $this->BO->getOID());
266
                    }
267
268
                    // handle the setting of MANY-TO-ONE relation values
269
                    if ($prop->getRelationType() == 'MANY-TO-ONE' && isset($row[$propName])) {
270
                        $this->BO->set($propObj->name, $row[$propName]);
271
                    }
272
                }
273
            }
274
        } 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...
275
            self::$logger->warn('Bad data stored in the table ['.$this->BO->getTableName().'], field ['.$propObj->name.'] bad value['.$row[$propObj->name].'], exception ['.$e->getMessage().']');
276
        } catch (PHPException $e) {
277
            // it is possible that the load failed due to the table not being up-to-date
278 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...
279
                $missingFields = $this->BO->findMissingFields();
280
281
                $count = count($missingFields);
282
283
                for ($i = 0; $i < $count; ++$i) {
284
                    $this->BO->addProperty($missingFields[$i]);
285
                }
286
287
                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!');
288
                self::$logger->debug('<<load');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<load'); 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...
289
290
                return;
291
            }
292
        }
293
294
        self::$logger->debug('<<load');
295
    }
296
297
    /**
298
     * (non-PHPdoc).
299
     *
300
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAllOldVersions()
301
     */
302 View Code Duplication
    public function loadAllOldVersions($OID)
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...
303
    {
304
        self::$logger->debug('>>loadAllOldVersions(OID=['.$OID.'])');
305
306
        $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...
307
308
        if (!$this->BO->getMaintainHistory()) {
309
            throw new RecordFoundException('loadAllOldVersions method called on an active record where no history is maintained!');
310
        }
311
312
        $sqlQuery = 'SELECT version_num FROM '.$this->BO->getTableName().'_history WHERE OID = \''.$OID.'\' ORDER BY version_num;';
313
314
        $this->BO->setLastQuery($sqlQuery);
315
316
        if (!$result = self::getConnection()->query($sqlQuery)) {
317
            throw new RecordNotFoundException('Failed to load object versions, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
318
            self::$logger->debug('<<loadAllOldVersions [0]');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<loadAllOldVersions [0]'); 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...
319
320
            return array();
321
        }
322
323
        // now build an array of objects to be returned
324
        $objects = array();
325
        $count = 0;
326
        $RecordClass = get_class($this->BO);
327
328
        while ($row = $result->fetchArray()) {
329
            try {
330
                $obj = new $RecordClass();
331
                $obj->load($OID, $row['version_num']);
332
                $objects[$count] = $obj;
333
                ++$count;
334
            } 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...
335
                // the resource not allowed will be absent from the list
336
            }
337
        }
338
339
        self::$logger->warn('<<loadAllOldVersions ['.count($objects).']');
340
341
        return $objects;
342
    }
343
344
    /**
345
     * (non-PHPdoc).
346
     *
347
     * @see Alpha\Model\ActiveRecordProviderInterface::loadByAttribute()
348
     */
349
    public function loadByAttribute($attribute, $value, $ignoreClassType = false, $loadAttributes = array())
350
    {
351
        self::$logger->debug('>>loadByAttribute(attribute=['.$attribute.'], value=['.$value.'], ignoreClassType=['.$ignoreClassType.'], 
352
            loadAttributes=['.var_export($loadAttributes, true).'])');
353
354
        if (count($loadAttributes) == 0) {
355
            $attributes = $this->BO->getPersistentAttributes();
356
        } else {
357
            $attributes = $loadAttributes;
358
        }
359
360
        $fields = '';
361
        foreach ($attributes as $att) {
362
            $fields .= $att.',';
363
        }
364
        $fields = mb_substr($fields, 0, -1);
365
366 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...
367
            $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE '.$attribute.' = :attribute AND classname = :classname LIMIT 1;';
368
        } else {
369
            $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE '.$attribute.' = :attribute LIMIT 1;';
370
        }
371
372
        self::$logger->debug('Query=['.$sqlQuery.']');
373
374
        $this->BO->setLastQuery($sqlQuery);
375
        $stmt = self::getConnection()->prepare($sqlQuery);
376
377
        $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...
378
379
        if ($stmt instanceof SQLite3Stmt) {
380
            if ($this->BO->getPropObject($attribute) instanceof Integer) {
381 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...
382
                    $stmt->bindValue(':attribute', $value, SQLITE3_INTEGER);
383
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
384
                } else {
385
                    $stmt->bindValue(':attribute', $value, SQLITE3_INTEGER);
386
                }
387 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...
388
                if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
389
                    $stmt->bindValue(':attribute', $value, SQLITE3_TEXT);
390
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
391
                } else {
392
                    $stmt->bindValue(':attribute', $value, SQLITE3_TEXT);
393
                }
394
            }
395
396
            $result = $stmt->execute();
397
398
            // there should only ever be one (or none)
399
            $row = $result->fetchArray(SQLITE3_ASSOC);
400
401
            $stmt->close();
402
        } else {
403
            self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
404
            if (!$this->BO->checkTableExists()) {
405
                $this->BO->makeTable();
406
407
                throw new RecordNotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], table did not exist so had to create!');
408
            }
409
410
            return;
411
        }
412
413 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...
414
            throw new RecordNotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], not found in database.');
415
            self::$logger->debug('<<loadByAttribute');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<loadByAttribute'); 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...
416
417
            return;
418
        }
419
420
        $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...
421
422
        // get the class attributes
423
        $reflection = new ReflectionClass(get_class($this->BO));
424
        $properties = $reflection->getProperties();
425
426
        try {
427 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...
428
                $propName = $propObj->name;
429
430
                if (isset($row[$propName])) {
431
                    // filter transient attributes
432
                    if (!in_array($propName, $this->BO->getTransientAttributes())) {
433
                        $this->BO->set($propName, $row[$propName]);
434
                    } elseif (!$propObj->isPrivate() && $this->BO->get($propName) != '' && $this->BO->getPropObject($propName) instanceof Relation) {
435
                        $prop = $this->BO->getPropObject($propName);
436
437
                        // handle the setting of ONE-TO-MANY relation values
438
                        if ($prop->getRelationType() == 'ONE-TO-MANY') {
439
                            $this->BO->set($propObj->name, $this->BO->getOID());
440
                        }
441
                    }
442
                }
443
            }
444
        } 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...
445
            self::$logger->warn('Bad data stored in the table ['.$this->BO->getTableName().'], field ['.$propObj->name.'] bad value['.$row[$propObj->name].'], exception ['.$e->getMessage().']');
446
        } catch (PHPException $e) {
447
            // it is possible that the load failed due to the table not being up-to-date
448 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...
449
                $missingFields = $this->BO->findMissingFields();
450
451
                $count = count($missingFields);
452
453
                for ($i = 0; $i < $count; ++$i) {
454
                    $this->BO->addProperty($missingFields[$i]);
455
                }
456
457
                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!');
458
                self::$logger->debug('<<loadByAttribute');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<loadByAttribute'); 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...
459
460
                return;
461
            }
462
        }
463
464
        self::$logger->debug('<<loadByAttribute');
465
    }
466
467
    /**
468
     * (non-PHPdoc).
469
     *
470
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAll()
471
     */
472 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...
473
    {
474
        self::$logger->debug('>>loadAll(start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
475
476
        // ensure that the field name provided in the orderBy param is legit
477
        try {
478
            $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...
479
        } catch (AlphaException $e) {
480
            throw new AlphaException('The field name ['.$orderBy.'] provided in the param orderBy does not exist on the class ['.get_class($this->BO).']');
481
        }
482
483
        if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
484
            if ($limit == 0) {
485
                $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE classname = \''.get_class($this->BO).'\' ORDER BY '.$orderBy.' '.$order.';';
486
            } else {
487
                $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE classname = \''.get_class($this->BO).'\' ORDER BY '.$orderBy.' '.$order.' LIMIT '.
488
                    $limit.' OFFSET '.$start.';';
489
            }
490
        } else {
491
            if ($limit == 0) {
492
                $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' ORDER BY '.$orderBy.' '.$order.';';
493
            } else {
494
                $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' ORDER BY '.$orderBy.' '.$order.' LIMIT '.$limit.' OFFSET '.$start.';';
495
            }
496
        }
497
498
        $this->BO->setLastQuery($sqlQuery);
499
500
        if (!$result = self::getConnection()->query($sqlQuery)) {
501
            throw new RecordNotFoundException('Failed to load object OIDs, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
502
            self::$logger->debug('<<loadAll [0]');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<loadAll [0]'); 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...
503
504
            return array();
505
        }
506
507
        // now build an array of objects to be returned
508
        $objects = array();
509
        $count = 0;
510
        $RecordClass = get_class($this->BO);
511
512
        while ($row = $result->fetchArray()) {
513
            try {
514
                $obj = new $RecordClass();
515
                $obj->load($row['OID']);
516
                $objects[$count] = $obj;
517
                ++$count;
518
            } 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...
519
                // the resource not allowed will be absent from the list
520
            }
521
        }
522
523
        self::$logger->debug('<<loadAll ['.count($objects).']');
524
525
        return $objects;
526
    }
527
528
    /**
529
     * (non-PHPdoc).
530
     *
531
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAllByAttribute()
532
     */
533
    public function loadAllByAttribute($attribute, $value, $start = 0, $limit = 0, $orderBy = 'OID', $order = 'ASC', $ignoreClassType = false, $constructorArgs = array())
534
    {
535
        self::$logger->debug('>>loadAllByAttribute(attribute=['.$attribute.'], value=['.$value.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.'], constructorArgs=['.print_r($constructorArgs, true).']');
536
537
        if ($start != 0 && $limit != 0) {
538
            $limit = ' LIMIT '.$limit.' OFFSET '.$start.';';
539
        } else {
540
            $limit = ';';
541
        }
542
543 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...
544
            $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName()." WHERE $attribute = :attribute AND classname = :classname ORDER BY ".$orderBy.' '.$order.$limit;
545
        } else {
546
            $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName()." WHERE $attribute = :attribute ORDER BY ".$orderBy.' '.$order.$limit;
547
        }
548
549
        $this->BO->setLastQuery($sqlQuery);
550
        self::$logger->debug($sqlQuery);
551
552
        $stmt = self::getConnection()->prepare($sqlQuery);
553
554
        $objects = array();
555
556
        if ($stmt instanceof SQLite3Stmt) {
557
            if ($this->BO->getPropObject($attribute) instanceof Integer) {
558 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...
559
                    $stmt->bindValue(':attribute', $value, SQLITE3_INTEGER);
560
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
561
                } else {
562
                    $stmt->bindValue(':attribute', $value, SQLITE3_INTEGER);
563
                }
564 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...
565
                if ($this->BO->isTableOverloaded()) {
566
                    $stmt->bindValue(':attribute', $value, SQLITE3_TEXT);
567
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
568
                } else {
569
                    $stmt->bindValue(':attribute', $value, SQLITE3_TEXT);
570
                }
571
            }
572
573
            $result = $stmt->execute();
574
575
            // now build an array of objects to be returned
576
            $count = 0;
577
            $RecordClass = get_class($this->BO);
578
579 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...
580
                try {
581
                    $argsCount = count($constructorArgs);
582
583
                    if ($argsCount < 1) {
584
                        $obj = new $RecordClass();
585
                    } else {
586
                        switch ($argsCount) {
587
                            case 1:
588
                                $obj = new $RecordClass($constructorArgs[0]);
589
                                break;
590
                            case 2:
591
                                $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1]);
592
                                break;
593
                            case 3:
594
                                $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2]);
595
                                break;
596
                            case 4:
597
                                $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2], $constructorArgs[3]);
598
                                break;
599
                            case 5:
600
                                $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2], $constructorArgs[3], $constructorArgs[4]);
601
                                break;
602
                            default:
603
                                throw new IllegalArguementException('Too many elements in the $constructorArgs array passed to the loadAllByAttribute method!');
604
                                break;
0 ignored issues
show
Unused Code introduced by
break; 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...
605
                        }
606
                    }
607
608
                    $obj->load($row['OID']);
609
                    $objects[$count] = $obj;
610
                    ++$count;
611
                } 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...
612
                    // the resource not allowed will be absent from the list
613
                }
614
            }
615
616
            $stmt->close();
617 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...
618
            self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
619
620
            if (!$this->BO->checkTableExists()) {
621
                $this->BO->makeTable();
622
623
                throw new RecordFoundException('Failed to load objects by attribute ['.$attribute.'] and value ['.$value.'], table did not exist so had to create!');
624
            }
625
626
            self::$logger->debug('<<loadAllByAttribute []');
627
628
            return array();
629
        }
630
631
        self::$logger->debug('<<loadAllByAttribute ['.count($objects).']');
632
633
        return $objects;
634
    }
635
636
    /**
637
     * (non-PHPdoc).
638
     *
639
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAllByAttributes()
640
     */
641
    public function loadAllByAttributes($attributes = array(), $values = array(), $start = 0, $limit = 0, $orderBy = 'OID', $order = 'ASC', $ignoreClassType = false, $constructorArgs = array())
642
    {
643
        self::$logger->debug('>>loadAllByAttributes(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'], start=['.
644
            $start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.'], constructorArgs=['.print_r($constructorArgs, true).']');
645
646
        $whereClause = ' WHERE';
647
648
        $count = count($attributes);
649
650
        for ($i = 0; $i < $count; ++$i) {
651
            $whereClause .= ' '.$attributes[$i].' = :'.$attributes[$i].' AND';
652
            self::$logger->debug($whereClause);
653
        }
654
655
        if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
656
            $whereClause .= ' classname = :classname AND';
657
        }
658
659
        // remove the last " AND"
660
        $whereClause = mb_substr($whereClause, 0, -4);
661
662
        if ($limit != 0) {
663
            $limit = ' LIMIT '.$limit.' OFFSET '.$start.';';
664
        } else {
665
            $limit = ';';
666
        }
667
668
        $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().$whereClause.' ORDER BY '.$orderBy.' '.$order.$limit;
669
670
        $this->BO->setLastQuery($sqlQuery);
671
672
        $stmt = self::getConnection()->prepare($sqlQuery);
673
674
        if ($stmt instanceof SQLite3Stmt) {
675
            // bind params where required attributes are provided
676
            if (count($attributes) > 0 && count($attributes) == count($values)) {
677
                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...
678
                    if (strcspn($values[$i], '0123456789') != strlen($values[$i])) {
679
                        $stmt->bindValue(':'.$attributes[$i], $values[$i], SQLITE3_INTEGER);
680
                    } else {
681
                        $stmt->bindValue(':'.$attributes[$i], $values[$i], SQLITE3_TEXT);
682
                    }
683
                }
684
            } else {
685
                // we'll still need to bind the "classname" for overloaded BOs...
686
                if ($this->BO->isTableOverloaded()) {
687
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
688
                }
689
            }
690
691
            $result = $stmt->execute();
692 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...
693
            self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
694
695
            if (!$this->BO->checkTableExists()) {
696
                $this->BO->makeTable();
697
698
                throw new RecordFoundException('Failed to load objects by attributes ['.var_export($attributes, true).'] and values ['.
699
                    var_export($values, true).'], table did not exist so had to create!');
700
            }
701
702
            self::$logger->debug('<<loadAllByAttributes []');
703
704
            return array();
705
        }
706
707
        // now build an array of objects to be returned
708
        $objects = array();
709
        $count = 0;
710
        $RecordClass = get_class($this->BO);
711
712 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...
713
            try {
714
                $argsCount = count($constructorArgs);
715
716
                if ($argsCount < 1) {
717
                    $obj = new $RecordClass();
718
                } else {
719
                    switch ($argsCount) {
720
                        case 1:
721
                            $obj = new $RecordClass($constructorArgs[0]);
722
                            break;
723
                        case 2:
724
                            $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1]);
725
                            break;
726
                        case 3:
727
                            $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2]);
728
                            break;
729
                        case 4:
730
                            $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2], $constructorArgs[3]);
731
                            break;
732
                        case 5:
733
                            $obj = new $RecordClass($constructorArgs[0], $constructorArgs[1], $constructorArgs[2], $constructorArgs[3], $constructorArgs[4]);
734
                            break;
735
                        default:
736
                            throw new IllegalArguementException('Too many elements in the $constructorArgs array passed to the loadAllByAttribute method!');
737
                            break;
0 ignored issues
show
Unused Code introduced by
break; 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...
738
                    }
739
                }
740
741
                $obj->load($row['OID']);
742
                $objects[$count] = $obj;
743
                ++$count;
744
            } 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...
745
                // the resource not allowed will be absent from the list
746
            }
747
        }
748
749
        $stmt->close();
750
751
        self::$logger->debug('<<loadAllByAttributes ['.count($objects).']');
752
753
        return $objects;
754
    }
755
756
    /**
757
     * (non-PHPdoc).
758
     *
759
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAllByDayUpdated()
760
     */
761 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...
762
    {
763
        self::$logger->debug('>>loadAllByDayUpdated(date=['.$date.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
764
765
        if ($start != 0 && $limit != 0) {
766
            $limit = ' LIMIT '.$limit.' OFFSET '.$start.';';
767
        } else {
768
            $limit = ';';
769
        }
770
771
        if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
772
            $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName()." WHERE updated_ts >= '".$date." 00:00:00' AND updated_ts <= '".$date." 23:59:59' AND classname = '".get_class($this->BO)."' ORDER BY ".$orderBy.' '.$order.$limit;
773
        } else {
774
            $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;
775
        }
776
777
        $this->BO->setLastQuery($sqlQuery);
778
779
        if (!$result = self::getConnection()->query($sqlQuery)) {
780
            throw new RecordNotFoundException('Failed to load object OIDs, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
781
            self::$logger->debug('<<loadAllByDayUpdated []');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<loadAllByDayUpdated []'); 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...
782
783
            return array();
784
        }
785
786
        // now build an array of objects to be returned
787
        $objects = array();
788
        $count = 0;
789
        $RecordClass = get_class($this->BO);
790
791
        while ($row = $result->fetchArray()) {
792
            $obj = new $RecordClass();
793
            $obj->load($row['OID']);
794
            $objects[$count] = $obj;
795
            ++$count;
796
        }
797
798
        self::$logger->debug('<<loadAllByDayUpdated ['.count($objects).']');
799
800
        return $objects;
801
    }
802
803
    /**
804
     * (non-PHPdoc).
805
     *
806
     * @see Alpha\Model\ActiveRecordProviderInterface::loadAllFieldValuesByAttribute()
807
     */
808 View Code Duplication
    public function loadAllFieldValuesByAttribute($attribute, $value, $returnAttribute, $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...
809
    {
810
        self::$logger->debug('>>loadAllFieldValuesByAttribute(attribute=['.$attribute.'], value=['.$value.'], returnAttribute=['.$returnAttribute.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
811
812
        if (!$ignoreClassType && $this->BO->isTableOverloaded()) {
813
            $sqlQuery = 'SELECT '.$returnAttribute.' FROM '.$this->BO->getTableName()." WHERE $attribute = '$value' AND classname = '".get_class($this->BO)."' ORDER BY OID ".$order.';';
814
        } else {
815
            $sqlQuery = 'SELECT '.$returnAttribute.' FROM '.$this->BO->getTableName()." WHERE $attribute = '$value' ORDER BY OID ".$order.';';
816
        }
817
818
        $this->BO->setLastQuery($sqlQuery);
819
820
        self::$logger->debug('lastQuery ['.$sqlQuery.']');
821
822
        if (!$result = self::getConnection()->query($sqlQuery)) {
823
            throw new RecordNotFoundException('Failed to load field ['.$returnAttribute.'] values, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
824
            self::$logger->debug('<<loadAllFieldValuesByAttribute []');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<...ValuesByAttribute []'); 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...
825
826
            return array();
827
        }
828
829
        // now build an array of attribute values to be returned
830
        $values = array();
831
        $count = 0;
832
        $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...
833
834
        while ($row = $result->fetchArray()) {
835
            $values[$count] = $row[$returnAttribute];
836
            ++$count;
837
        }
838
839
        self::$logger->debug('<<loadAllFieldValuesByAttribute ['.count($values).']');
840
841
        return $values;
842
    }
843
844
    /**
845
     * (non-PHPdoc).
846
     *
847
     * @see Alpha\Model\ActiveRecordProviderInterface::save()
848
     */
849
    public function save()
850
    {
851
        self::$logger->debug('>>save()');
852
853
        $config = ConfigProvider::getInstance();
854
        $sessionProvider = $config->get('session.provider.name');
855
        $session = SessionProviderFactory::getInstance($sessionProvider);
856
857
        // get the class attributes
858
        $reflection = new ReflectionClass(get_class($this->BO));
859
        $properties = $reflection->getProperties();
860
        $sqlQuery = '';
0 ignored issues
show
Unused Code introduced by
$sqlQuery 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...
861
        $stmt = null;
0 ignored issues
show
Unused Code introduced by
$stmt 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...
862
863
        if ($this->BO->getVersion() != $this->BO->getVersionNumber()->getValue()) {
864
            throw new LockingException('Could not save the object as it has been updated by another user.  Please try saving again.');
865
866
            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...
867
        }
868
869
        // set the "updated by" fields, we can only set the user id if someone is logged in
870
        if ($session->get('currentUser') != null) {
871
            $this->BO->set('updated_by', $session->get('currentUser')->getOID());
872
        }
873
874
        $this->BO->set('updated_ts', new Timestamp(date('Y-m-d H:i:s')));
875
876
        // check to see if it is a transient object that needs to be inserted
877
        if ($this->BO->isTransient()) {
878
            $savedFields = array();
879
            $sqlQuery = 'INSERT INTO '.$this->BO->getTableName().' (';
880
881 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...
882
                $propName = $propObj->name;
883
                if (!in_array($propName, $this->BO->getTransientAttributes())) {
884
                    // Skip the OID, database auto number takes care of this.
885
                    if ($propName != 'OID' && $propName != 'version_num') {
886
                        $sqlQuery .= "$propName,";
887
                        $savedFields[] = $propName;
888
                    }
889
890
                    if ($propName == 'version_num') {
891
                        $sqlQuery .= 'version_num,';
892
                        $savedFields[] = 'version_num';
893
                    }
894
                }
895
            }
896
            if ($this->BO->isTableOverloaded()) {
897
                $sqlQuery .= 'classname,';
898
            }
899
900
            $sqlQuery = rtrim($sqlQuery, ',');
901
902
            $sqlQuery .= ') VALUES (';
903
904
            foreach ($savedFields as $savedField) {
905
                $sqlQuery .= ':'.$savedField.',';
906
            }
907
908
            if ($this->BO->isTableOverloaded()) {
909
                $sqlQuery .= ':classname,';
910
            }
911
912
            $sqlQuery = rtrim($sqlQuery, ',').')';
913
914
            $this->BO->setLastQuery($sqlQuery);
915
            self::$logger->debug('Query ['.$sqlQuery.']');
916
917
            $stmt = self::getConnection()->prepare($sqlQuery);
918
919
            if ($stmt instanceof SQLite3Stmt) {
920 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...
921
                    if ($this->BO->get($savedField) instanceof Integer) {
922
                        $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_INTEGER);
923
                    } else {
924
                        $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_TEXT);
925
                    }
926
                }
927
928
                if ($this->BO->isTableOverloaded()) {
929
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
930
                }
931
932
                $stmt->bindValue(':version_num', 1, SQLITE3_INTEGER); // on an initial save, this will always be 1
933
                $this->BO->set('version_num', 1);
934
935
                try {
936
                    $stmt->execute();
937
                } catch (Exception $e) {
938
                    if (self::getConnection()->lastErrorCode() == 19) {
939
                        throw new ValidationException('Unique key violation while trying to save object, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
940 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...
941
                        throw new FailedSaveException('Failed to save object, exception ['.$e->getMessage().'], DB error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
942
                    }
943
                }
944 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...
945
                throw new FailedSaveException('Failed to save object, exception ['.$e->getMessage().'], DB error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
946
            }
947
        } else {
948
            // assume that it is a persistent object that needs to be updated
949
            $savedFields = array();
950
            $sqlQuery = 'UPDATE '.$this->BO->getTableName().' SET ';
951
952 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...
953
                $propName = $propObj->name;
954
                if (!in_array($propName, $this->BO->getTransientAttributes())) {
955
                    // Skip the OID, database auto number takes care of this.
956
                    if ($propName != 'OID' && $propName != 'version_num') {
957
                        $sqlQuery .= "$propName = :$propName,";
958
                        $savedFields[] = $propName;
959
                    }
960
961
                    if ($propName == 'version_num') {
962
                        $sqlQuery .= 'version_num = :version_num,';
963
                        $savedFields[] = 'version_num';
964
                    }
965
                }
966
            }
967
968
            if ($this->BO->isTableOverloaded()) {
969
                $sqlQuery .= 'classname = :classname,';
970
            }
971
972
            $sqlQuery = rtrim($sqlQuery, ',');
973
974
            $sqlQuery .= ' WHERE OID=:OID;';
975
976
            $this->BO->setLastQuery($sqlQuery);
977
            $stmt = self::getConnection()->prepare($sqlQuery);
978
979
            if ($stmt instanceof SQLite3Stmt) {
980 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...
981
                    if ($this->BO->get($savedField) instanceof Integer) {
982
                        $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_INTEGER);
983
                    } else {
984
                        $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_TEXT);
985
                    }
986
                }
987
988
                if ($this->BO->isTableOverloaded()) {
989
                    $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
990
                }
991
992
                $stmt->bindValue(':OID', $this->BO->getOID(), SQLITE3_INTEGER);
993
994
                $temp = $this->BO->getVersionNumber()->getValue();
995
                $this->BO->set('version_num', $temp + 1);
996
                $stmt->bindValue(':version_num', $temp + 1, SQLITE3_INTEGER);
997
998
                $stmt->execute();
999
            } else {
1000
                throw new FailedSaveException('Failed to save object, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
1001
            }
1002
        }
1003
1004
        if ($stmt != null && $stmt != false) {
1005
            // populate the updated OID in case we just done an insert
1006
            if ($this->BO->isTransient()) {
1007
                $this->BO->setOID(self::getConnection()->lastInsertRowID());
1008
            }
1009
1010
            try {
1011 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...
1012
                    $propName = $propObj->name;
1013
1014
                    if ($this->BO->getPropObject($propName) instanceof Relation) {
1015
                        $prop = $this->BO->getPropObject($propName);
1016
1017
                        // handle the saving of MANY-TO-MANY relation values
1018
                        if ($prop->getRelationType() == 'MANY-TO-MANY' && count($prop->getRelatedOIDs()) > 0) {
1019
                            try {
1020
                                try {
1021
                                    // check to see if the rel is on this class
1022
                                    $side = $prop->getSide(get_class($this->BO));
1023
                                } 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...
1024
                                    $side = $prop->getSide(get_parent_class($this->BO));
1025
                                }
1026
1027
                                $lookUp = $prop->getLookup();
1028
1029
                                // first delete all of the old RelationLookup objects for this rel
1030
                                try {
1031
                                    if ($side == 'left') {
1032
                                        $lookUp->deleteAllByAttribute('leftID', $this->BO->getOID());
1033
                                    } else {
1034
                                        $lookUp->deleteAllByAttribute('rightID', $this->BO->getOID());
1035
                                    }
1036
                                } catch (Exception $e) {
1037
                                    throw new FailedSaveException('Failed to delete old RelationLookup objects on the table ['.$prop->getLookup()->getTableName().'], error is ['.$e->getMessage().']');
1038
                                }
1039
1040
                                $OIDs = $prop->getRelatedOIDs();
1041
1042
                                if (isset($OIDs) && !empty($OIDs[0])) {
1043
                                    // now for each posted OID, create a new RelationLookup record and save
1044
                                    foreach ($OIDs as $oid) {
1045
                                        $newLookUp = new RelationLookup($lookUp->get('leftClassName'), $lookUp->get('rightClassName'));
1046
                                        if ($side == 'left') {
1047
                                            $newLookUp->set('leftID', $this->BO->getOID());
1048
                                            $newLookUp->set('rightID', $oid);
1049
                                        } else {
1050
                                            $newLookUp->set('rightID', $this->BO->getOID());
1051
                                            $newLookUp->set('leftID', $oid);
1052
                                        }
1053
                                        $newLookUp->save();
1054
                                    }
1055
                                }
1056
                            } catch (Exception $e) {
1057
                                throw new FailedSaveException('Failed to update a MANY-TO-MANY relation on the object, error is ['.$e->getMessage().']');
1058
1059
                                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...
1060
                            }
1061
                        }
1062
1063
                        // handle the saving of ONE-TO-MANY relation values
1064
                        if ($prop->getRelationType() == 'ONE-TO-MANY') {
1065
                            $prop->setValue($this->BO->getOID());
1066
                        }
1067
                    }
1068
                }
1069
            } catch (Exception $e) {
1070
                throw new FailedSaveException('Failed to save object, error is ['.$e->getMessage().']');
1071
1072
                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...
1073
            }
1074
1075
            $stmt->close();
1076
        } else {
1077
            // there has been an error, so decrement the version number back
1078
            $temp = $this->BO->getVersionNumber()->getValue();
1079
            $this->BO->set('version_num', $temp - 1);
1080
1081
            throw new FailedSaveException('Failed to save object, SQLite error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
1082
        }
1083
1084
        if ($this->BO->getMaintainHistory()) {
1085
            $this->BO->saveHistory();
1086
        }
1087
    }
1088
1089
    /**
1090
     * (non-PHPdoc).
1091
     *
1092
     * @see Alpha\Model\ActiveRecordProviderInterface::saveAttribute()
1093
     */
1094
    public function saveAttribute($attribute, $value)
1095
    {
1096
        self::$logger->debug('>>saveAttribute(attribute=['.$attribute.'], value=['.$value.'])');
1097
1098
        // assume that it is a persistent object that needs to be updated
1099
        $sqlQuery = 'UPDATE '.$this->BO->getTableName().' SET '.$attribute.'=:attribute, version_num =:version WHERE OID=:OID;';
1100
1101
        $this->BO->setLastQuery($sqlQuery);
1102
        $stmt = self::getConnection()->prepare($sqlQuery);
1103
1104
        $newVersionNumber = $this->BO->getVersionNumber()->getValue() + 1;
1105
1106
        if ($stmt instanceof SQLite3Stmt) {
1107
            if ($this->BO->getPropObject($attribute) instanceof Integer) {
1108
                $stmt->bindValue(':attribute', $value, SQLITE3_INTEGER);
1109
            } else {
1110
                $stmt->bindValue(':attribute', $value, SQLITE3_TEXT);
1111
            }
1112
1113
            $stmt->bindValue(':version', $newVersionNumber, SQLITE3_INTEGER);
1114
            $stmt->bindValue(':OID', $this->BO->getOID(), SQLITE3_INTEGER);
1115
1116
            $stmt->execute();
1117 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...
1118
            throw new FailedSaveException('Failed to save attribute, error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
1119
        }
1120
1121
        $stmt->close();
1122
1123
        $this->BO->set($attribute, $value);
1124
        $this->BO->set('version_num', $newVersionNumber);
1125
1126
        if ($this->BO->getMaintainHistory()) {
1127
            $this->BO->saveHistory();
1128
        }
1129
1130
        self::$logger->debug('<<saveAttribute');
1131
    }
1132
1133
    /**
1134
     * (non-PHPdoc).
1135
     *
1136
     * @see Alpha\Model\ActiveRecordProviderInterface::saveHistory()
1137
     */
1138
    public function saveHistory()
1139
    {
1140
        self::$logger->debug('>>saveHistory()');
1141
1142
        // get the class attributes
1143
        $reflection = new ReflectionClass(get_class($this->BO));
1144
        $properties = $reflection->getProperties();
1145
        $sqlQuery = '';
0 ignored issues
show
Unused Code introduced by
$sqlQuery 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...
1146
        $stmt = null;
0 ignored issues
show
Unused Code introduced by
$stmt 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...
1147
1148
        $savedFields = array();
1149
        $attributeNames = array();
1150
        $attributeValues = array();
1151
1152
        $sqlQuery = 'INSERT INTO '.$this->BO->getTableName().'_history (';
1153
1154 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...
1155
            $propName = $propObj->name;
1156
            if (!in_array($propName, $this->BO->getTransientAttributes())) {
1157
                $sqlQuery .= "$propName,";
1158
                $attributeNames[] = $propName;
1159
                $attributeValues[] = $this->BO->get($propName);
1160
                $savedFields[] = $propName;
1161
            }
1162
        }
1163
1164
        if ($this->BO->isTableOverloaded()) {
1165
            $sqlQuery .= 'classname,';
1166
        }
1167
1168
        $sqlQuery = rtrim($sqlQuery, ',');
1169
1170
        $sqlQuery .= ') VALUES (';
1171
1172
        foreach ($savedFields as $savedField) {
1173
            $sqlQuery .= ':'.$savedField.',';
1174
        }
1175
1176
        if ($this->BO->isTableOverloaded()) {
1177
            $sqlQuery .= ':classname,';
1178
        }
1179
1180
        $sqlQuery = rtrim($sqlQuery, ',').')';
1181
1182
        $this->BO->setLastQuery($sqlQuery);
1183
        self::$logger->debug('Query ['.$sqlQuery.']');
1184
1185
        $stmt = self::getConnection()->prepare($sqlQuery);
1186
1187
        if ($stmt instanceof SQLite3Stmt) {
1188 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...
1189
                if ($this->BO->get($savedField) instanceof Integer) {
1190
                    $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_INTEGER);
1191
                } else {
1192
                    $stmt->bindValue(':'.$savedField, $this->BO->get($savedField), SQLITE3_TEXT);
1193
                }
1194
            }
1195
1196
            if ($this->BO->isTableOverloaded()) {
1197
                $stmt->bindValue(':classname', get_class($this->BO), SQLITE3_TEXT);
1198
            }
1199
1200
            $result = $stmt->execute();
0 ignored issues
show
Unused Code introduced by
$result 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...
1201 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...
1202
            throw new FailedSaveException('Failed to save object history, error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
1203
        }
1204
    }
1205
1206
    /**
1207
     * (non-PHPdoc).
1208
     *
1209
     * @see Alpha\Model\ActiveRecordProviderInterface::delete()
1210
     */
1211
    public function delete()
1212
    {
1213
        self::$logger->debug('>>delete()');
1214
1215
        $sqlQuery = 'DELETE FROM '.$this->BO->getTableName().' WHERE OID = :OID;';
1216
1217
        $this->BO->setLastQuery($sqlQuery);
1218
1219
        $stmt = self::getConnection()->prepare($sqlQuery);
1220
1221
        if ($stmt instanceof SQLite3Stmt) {
1222
            $stmt->bindValue(':OID', $this->BO->getOID(), SQLITE3_INTEGER);
1223
            $stmt->execute();
1224
            self::$logger->debug('Deleted the object ['.$this->BO->getOID().'] of class ['.get_class($this->BO).']');
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
            throw new FailedDeleteException('Failed to delete object ['.$this->BO->getOID().'], error is ['.self::getLastDatabaseError().'], query ['.$this->BO->getLastQuery().']');
1227
        }
1228
1229
        $stmt->close();
1230
1231
        self::$logger->debug('<<delete');
1232
    }
1233
1234
    /**
1235
     * (non-PHPdoc).
1236
     *
1237
     * @see Alpha\Model\ActiveRecordProviderInterface::getVersion()
1238
     */
1239
    public function getVersion()
1240
    {
1241
        self::$logger->debug('>>getVersion()');
1242
1243
        $sqlQuery = 'SELECT version_num FROM '.$this->BO->getTableName().' WHERE OID = :OID;';
1244
        $this->BO->setLastQuery($sqlQuery);
1245
1246
        $stmt = self::getConnection()->prepare($sqlQuery);
1247
1248
        if ($stmt instanceof SQLite3Stmt) {
1249
            $stmt->bindValue(':OID', $this->BO->getOID(), SQLITE3_INTEGER);
1250
1251
            $result = $stmt->execute();
1252
1253
            // there should only ever be one (or none)
1254
            $row = $result->fetchArray(SQLITE3_ASSOC);
1255
1256
            $stmt->close();
1257 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...
1258
            self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
1259
            if (!$this->BO->checkTableExists()) {
1260
                $this->BO->makeTable();
1261
1262
                throw new RecordNotFoundException('Failed to get the version number, table did not exist so had to create!');
1263
            }
1264
1265
            return;
1266
        }
1267
1268 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...
1269
            self::$logger->debug('<<getVersion [0]');
1270
1271
            return 0;
1272
        } else {
1273
            $version_num = $row['version_num'];
1274
1275
            self::$logger->debug('<<getVersion ['.$version_num.']');
1276
1277
            return $version_num;
1278
        }
1279
    }
1280
1281
    /**
1282
     * (non-PHPdoc).
1283
     *
1284
     * @see Alpha\Model\ActiveRecordProviderInterface::makeTable()
1285
     */
1286
    public function makeTable()
1287
    {
1288
        self::$logger->debug('>>makeTable()');
1289
1290
        $sqlQuery = 'CREATE TABLE '.$this->BO->getTableName().' (OID INTEGER PRIMARY KEY,';
1291
1292
        // get the class attributes
1293
        $reflection = new ReflectionClass(get_class($this->BO));
1294
        $properties = $reflection->getProperties();
1295
1296
        $foreignKeys = array();
1297
1298
        foreach ($properties as $propObj) {
1299
            $propName = $propObj->name;
1300
1301
            if (!in_array($propName, $this->BO->getTransientAttributes()) && $propName != 'OID') {
1302
                $propReflect = new ReflectionClass($this->BO->getPropObject($propName));
1303
                $propClass = $propReflect->getShortName();
1304
1305
                switch (mb_strtoupper($propClass)) {
1306
                    case 'INTEGER':
1307
                        // special properties for RelationLookup OIDs
1308
                        if ($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID')) {
1309
                            $sqlQuery .= "$propName INTEGER(".$this->BO->getPropObject($propName)->getSize().') NOT NULL,';
1310
                        } else {
1311
                            $sqlQuery .= "$propName INTEGER(".$this->BO->getPropObject($propName)->getSize().'),';
1312
                        }
1313
                    break;
1314
                    case 'DOUBLE':
1315
                        $sqlQuery .= "$propName REAL(".$this->BO->getPropObject($propName)->getSize(true).'),';
1316
                    break;
1317
                    case 'STRING':
1318
                        $sqlQuery .= "$propName TEXT(".$this->BO->getPropObject($propName)->getSize().'),';
1319
                    break;
1320
                    case 'TEXT':
1321
                        $sqlQuery .= "$propName TEXT,";
1322
                    break;
1323
                    case 'BOOLEAN':
1324
                        $sqlQuery .= "$propName INTEGER(1) DEFAULT '0',";
1325
                    break;
1326
                    case 'DATE':
1327
                        $sqlQuery .= "$propName TEXT,";
1328
                    break;
1329
                    case 'TIMESTAMP':
1330
                        $sqlQuery .= "$propName TEXT,";
1331
                    break;
1332
                    case 'ENUM':
1333
                        $sqlQuery .= "$propName TEXT,";
1334
                    break;
1335
                    case 'DENUM':
1336
                        $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\...Model\Type\String>|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...
1337
                        $sqlQuery .= "$propName INTEGER(11),";
1338
                    break;
1339
                    case 'RELATION':
1340
                        $sqlQuery .= "$propName INTEGER(11),";
1341
1342
                        $rel = $this->BO->getPropObject($propName);
1343
1344
                        $relatedField = $rel->getRelatedClassField();
1345
                        $relatedClass = $rel->getRelatedClass();
1346
                        $relatedBO = new $relatedClass();
1347
                        $tableName = $relatedBO->getTableName();
1348
                        $foreignKeys[$propName] = array($tableName, $relatedField);
1349
                    break;
1350
                    default:
1351
                        $sqlQuery .= '';
1352
                    break;
1353
                }
1354
            }
1355
        }
1356
        if ($this->BO->isTableOverloaded()) {
1357
            $sqlQuery .= 'classname TEXT(100)';
1358
        } else {
1359
            $sqlQuery = mb_substr($sqlQuery, 0, -1);
1360
        }
1361
1362
        if (count($foreignKeys) > 0) {
1363 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...
1364
                $sqlQuery .= ', FOREIGN KEY ('.$field.') REFERENCES '.$related[0].'('.$related[1].')';
1365
            }
1366
        }
1367
1368
        if (count($this->foreignKeys) > 0) {
1369 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...
1370
                $sqlQuery .= ', FOREIGN KEY ('.$field.') REFERENCES '.$related[0].'('.$related[1].')';
1371
            }
1372
        }
1373
1374
        $sqlQuery .= ');';
1375
1376
        $this->BO->setLastQuery($sqlQuery);
1377
1378
        if (!self::getConnection()->exec($sqlQuery)) {
1379
            throw new AlphaException('Failed to create the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.self::getLastDatabaseError().']');
1380
            self::$logger->debug('<<makeTable');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<makeTable'); 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...
1381
        }
1382
1383
        // check the table indexes if any additional ones required
1384
        $this->checkIndexes();
1385
1386
        if ($this->BO->getMaintainHistory()) {
1387
            $this->BO->makeHistoryTable();
1388
        }
1389
1390
        self::$logger->debug('<<makeTable');
1391
    }
1392
1393
    /**
1394
     * (non-PHPdoc).
1395
     *
1396
     * @see Alpha\Model\ActiveRecordProviderInterface::makeHistoryTable()
1397
     */
1398
    public function makeHistoryTable()
1399
    {
1400
        self::$logger->debug('>>makeHistoryTable()');
1401
1402
        $sqlQuery = 'CREATE TABLE '.$this->BO->getTableName().'_history (OID INTEGER NOT NULL,';
1403
1404
        // get the class attributes
1405
        $reflection = new ReflectionClass(get_class($this->BO));
1406
        $properties = $reflection->getProperties();
1407
1408
        foreach ($properties as $propObj) {
1409
            $propName = $propObj->name;
1410
1411 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...
1412
                $propReflect = new ReflectionClass($this->BO->getPropObject($propName));
1413
                $propClass = $propReflect->getShortName();
1414
1415
                switch (mb_strtoupper($propClass)) {
1416
                    case 'INTEGER':
1417
                        // special properties for RelationLookup OIDs
1418
                        if ($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID')) {
1419
                            $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().') NOT NULL,';
1420
                        } else {
1421
                            $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().'),';
1422
                        }
1423
                    break;
1424
                    case 'DOUBLE':
1425
                        $sqlQuery .= "$propName REAL(".$this->BO->getPropObject($propName)->getSize(true).'),';
1426
                    break;
1427
                    case 'STRING':
1428
                        $sqlQuery .= "$propName TEXT(".$this->BO->getPropObject($propName)->getSize().'),';
1429
                    break;
1430
                    case 'TEXT':
1431
                        $sqlQuery .= "$propName TEXT,";
1432
                    break;
1433
                    case 'BOOLEAN':
1434
                        $sqlQuery .= "$propName INTEGER(1) DEFAULT '0',";
1435
                    break;
1436
                    case 'DATE':
1437
                        $sqlQuery .= "$propName TEXT,";
1438
                    break;
1439
                    case 'TIMESTAMP':
1440
                        $sqlQuery .= "$propName TEXT,";
1441
                    break;
1442
                    case 'ENUM':
1443
                        $sqlQuery .= "$propName TEXT,";
1444
                    break;
1445
                    case 'DENUM':
1446
                        $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\...Model\Type\String>|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...
1447
                        $sqlQuery .= "$propName INTEGER(11),";
1448
                    break;
1449
                    case 'RELATION':
1450
                        $sqlQuery .= "$propName INTEGER(11),";
1451
                    break;
1452
                    default:
1453
                        $sqlQuery .= '';
1454
                    break;
1455
                }
1456
            }
1457
        }
1458
1459
        if ($this->BO->isTableOverloaded()) {
1460
            $sqlQuery .= 'classname TEXT(100),';
1461
        }
1462
1463
        $sqlQuery .= 'PRIMARY KEY (OID, version_num));';
1464
1465
        $this->BO->setLastQuery($sqlQuery);
1466
1467
        if (!$result = self::getConnection()->query($sqlQuery)) {
1468
            throw new AlphaException('Failed to create the table ['.$this->BO->getTableName().'_history] for the class ['.get_class($this->BO).'], database error is ['.self::getLastDatabaseError().']');
1469
            self::$logger->debug('<<makeHistoryTable');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<makeHistoryTable'); 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...
1470
        }
1471
1472
        self::$logger->debug('<<makeHistoryTable');
1473
    }
1474
1475
    /**
1476
     * (non-PHPdoc).
1477
     *
1478
     * @see Alpha\Model\ActiveRecordProviderInterface::rebuildTable()
1479
     */
1480 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...
1481
    {
1482
        self::$logger->debug('>>rebuildTable()');
1483
1484
        // the use of "IF EXISTS" here requires SQLite 3.3.0 or above.
1485
        $sqlQuery = 'DROP TABLE IF EXISTS '.$this->BO->getTableName().';';
1486
1487
        $this->BO->setLastQuery($sqlQuery);
1488
1489
        if (!$result = self::getConnection()->query($sqlQuery)) {
1490
            throw new AlphaException('Failed to drop the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.self::getLastDatabaseError().']');
1491
            self::$logger->debug('<<rebuildTable');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<rebuildTable'); 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...
1492
        }
1493
1494
        $this->BO->makeTable();
1495
1496
        self::$logger->debug('<<rebuildTable');
1497
    }
1498
1499
    /**
1500
     * (non-PHPdoc).
1501
     *
1502
     * @see Alpha\Model\ActiveRecordProviderInterface::dropTable()
1503
     */
1504 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...
1505
    {
1506
        self::$logger->debug('>>dropTable()');
1507
1508
        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...
1509
            $tableName = $this->BO->getTableName();
1510
        }
1511
1512
        // the use of "IF EXISTS" here requires SQLite 3.3.0 or above.
1513
        $sqlQuery = 'DROP TABLE IF EXISTS '.$tableName.';';
1514
1515
        $this->BO->setLastQuery($sqlQuery);
1516
1517
        if (!$result = self::getConnection()->query($sqlQuery)) {
1518
            throw new AlphaException('Failed to drop the table ['.$tableName.'] for the class ['.get_class($this->BO).'], query is ['.$this->BO->getLastQuery().']');
1519
            self::$logger->debug('<<dropTable');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<dropTable'); 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...
1520
        }
1521
1522
        if ($this->BO->getMaintainHistory()) {
1523
            $sqlQuery = 'DROP TABLE IF EXISTS '.$tableName.'_history;';
1524
1525
            $this->BO->setLastQuery($sqlQuery);
1526
1527
            if (!$result = self::getConnection()->query($sqlQuery)) {
1528
                throw new AlphaException('Failed to drop the table ['.$tableName.'_history] for the class ['.get_class($this->BO).'], query is ['.$this->BO->getLastQuery().']');
1529
                self::$logger->debug('<<dropTable');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<dropTable'); 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...
1530
            }
1531
        }
1532
1533
        self::$logger->debug('<<dropTable');
1534
    }
1535
1536
    /**
1537
     * (non-PHPdoc).
1538
     *
1539
     * @see Alpha\Model\ActiveRecordProviderInterface::addProperty()
1540
     */
1541
    public function addProperty($propName)
1542
    {
1543
        self::$logger->debug('>>addProperty(propName=['.$propName.'])');
1544
1545
        $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD ';
1546
1547
        if ($this->isTableOverloaded() && $propName == 'classname') {
1548
            $sqlQuery .= 'classname TEXT(100)';
1549 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...
1550
            if (!in_array($propName, $this->BO->getDefaultAttributes()) && !in_array($propName, $this->BO->getTransientAttributes())) {
1551
                $reflection = new ReflectionClass($this->BO->getPropObject($propName));
1552
                $propClass = $reflection->getShortName();
1553
1554
                switch (mb_strtoupper($propClass)) {
1555
                    case 'INTEGER':
1556
                        // special properties for RelationLookup OIDs
1557
                        if ($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID')) {
1558
                            $sqlQuery .= "$propName INTEGER(".$this->BO->getPropObject($propName)->getSize().') NOT NULL,';
1559
                        } else {
1560
                            $sqlQuery .= "$propName INTEGER(".$this->BO->getPropObject($propName)->getSize().'),';
1561
                        }
1562
                    break;
1563
                    case 'DOUBLE':
1564
                        $sqlQuery .= "$propName REAL(".$this->BO->getPropObject($propName)->getSize(true).'),';
1565
                    break;
1566
                    case 'STRING':
1567
                        $sqlQuery .= "$propName TEXT(".$this->BO->getPropObject($propName)->getSize().'),';
1568
                    break;
1569
                    case 'TEXT':
1570
                        $sqlQuery .= "$propName TEXT,";
1571
                    break;
1572
                    case 'BOOLEAN':
1573
                        $sqlQuery .= "$propName INTEGER(1) DEFAULT '0',";
1574
                    break;
1575
                    case 'DATE':
1576
                        $sqlQuery .= "$propName TEXT,";
1577
                    break;
1578
                    case 'TIMESTAMP':
1579
                        $sqlQuery .= "$propName TEXT,";
1580
                    break;
1581
                    case 'ENUM':
1582
                        $sqlQuery .= "$propName TEXT,";
1583
                    break;
1584
                    case 'DENUM':
1585
                        $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\...Model\Type\String>|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...
1586
                        $sqlQuery .= "$propName INTEGER(11),";
1587
                    break;
1588
                    case 'RELATION':
1589
                        $sqlQuery .= "$propName INTEGER(11),";
1590
                    break;
1591
                    default:
1592
                        $sqlQuery .= '';
1593
                    break;
1594
                }
1595
            }
1596
        }
1597
1598
        $this->BO->setLastQuery($sqlQuery);
1599
1600
        if (!$result = self::getConnection()->query($sqlQuery)) {
1601
            throw new AlphaException('Failed to add the new attribute ['.$propName.'] to the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1602
            self::$logger->debug('<<addProperty');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<addProperty'); 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...
1603
        } else {
1604
            self::$logger->info('Successfully added the ['.$propName.'] column onto the ['.$this->BO->getTableName().'] table for the class ['.get_class($this->BO).']');
1605
        }
1606
1607 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...
1608
            $sqlQuery = str_replace($this->BO->getTableName(), $this->BO->getTableName().'_history', $sqlQuery);
1609
1610
            if (!$result = self::getConnection()->query($sqlQuery)) {
1611
                throw new AlphaException('Failed to add the new attribute ['.$propName.'] to the table ['.$this->BO->getTableName().'_history], query is ['.$this->BO->getLastQuery().']');
1612
                self::$logger->debug('<<addProperty');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<addProperty'); 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...
1613
            } else {
1614
                self::$logger->info('Successfully added the ['.$propName.'] column onto the ['.$this->BO->getTableName().'_history] table for the class ['.get_class($this->BO).']');
1615
            }
1616
        }
1617
1618
        self::$logger->debug('<<addProperty');
1619
    }
1620
1621
    /**
1622
     * (non-PHPdoc).
1623
     *
1624
     * @see Alpha\Model\ActiveRecordProviderInterface::getMAX()
1625
     */
1626 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...
1627
    {
1628
        self::$logger->debug('>>getMAX()');
1629
1630
        $sqlQuery = 'SELECT MAX(OID) AS max_OID FROM '.$this->BO->getTableName();
1631
1632
        $this->BO->setLastQuery($sqlQuery);
1633
1634
        try {
1635
            $result = $this->BO->query($sqlQuery);
1636
1637
            $row = $result[0];
1638
1639
            if (isset($row['max_OID'])) {
1640
                self::$logger->debug('<<getMAX ['.$row['max_OID'].']');
1641
1642
                return $row['max_OID'];
1643
            } else {
1644
                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().']');
1645
            }
1646
        } catch (Exception $e) {
1647
            throw new AlphaException($e->getMessage());
1648
            self::$logger->debug('<<getMAX [0]');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<getMAX [0]'); 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...
1649
1650
            return 0;
1651
        }
1652
    }
1653
1654
    /**
1655
     * (non-PHPdoc).
1656
     *
1657
     * @see Alpha\Model\ActiveRecordProviderInterface::getCount()
1658
     */
1659 View Code Duplication
    public function getCount($attributes = array(), $values = array())
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...
1660
    {
1661
        self::$logger->debug('>>getCount(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'])');
1662
1663
        if ($this->BO->isTableOverloaded()) {
1664
            $whereClause = ' WHERE classname = \''.get_class($this->BO).'\' AND';
1665
        } else {
1666
            $whereClause = ' WHERE';
1667
        }
1668
1669
        $count = count($attributes);
1670
1671
        for ($i = 0; $i < $count; ++$i) {
1672
            $whereClause .= ' '.$attributes[$i].' = \''.$values[$i].'\' AND';
1673
            self::$logger->debug($whereClause);
1674
        }
1675
        // remove the last " AND"
1676
        $whereClause = mb_substr($whereClause, 0, -4);
1677
1678
        if ($whereClause != ' WHERE') {
1679
            $sqlQuery = 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName().$whereClause;
1680
        } else {
1681
            $sqlQuery = 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName();
1682
        }
1683
1684
        $this->BO->setLastQuery($sqlQuery);
1685
1686
        if (!$result = self::getConnection()->query($sqlQuery)) {
1687
            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().']');
1688
1689
            self::$logger->debug('<<getCount [0]');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<getCount [0]'); 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...
1690
1691
            return 0;
1692
        } else {
1693
            $row = $result->fetchArray(SQLITE3_ASSOC);
1694
1695
            self::$logger->debug('<<getCount ['.$row['class_count'].']');
1696
1697
            return $row['class_count'];
1698
        }
1699
    }
1700
1701
    /**
1702
     * (non-PHPdoc).
1703
     *
1704
     * @see Alpha\Model\ActiveRecordProviderInterface::getHistoryCount()
1705
     */
1706
    public function getHistoryCount()
1707
    {
1708
        self::$logger->debug('>>getHistoryCount()');
1709
1710
        if (!$this->BO->getMaintainHistory()) {
1711
            throw new AlphaException('getHistoryCount method called on a DAO where no history is maintained!');
1712
        }
1713
1714
        $sqlQuery = 'SELECT COUNT(OID) AS object_count FROM '.$this->BO->getTableName().'_history WHERE OID='.$this->BO->getOID();
1715
1716
        $this->BO->setLastQuery($sqlQuery);
1717
        self::$logger->debug('query ['.$sqlQuery.']');
1718
1719
        if (!$result = self::getConnection()->query($sqlQuery)) {
1720
            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().']');
1721
1722
            self::$logger->debug('<<getHistoryCount [0]');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<getHistoryCount [0]'); 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...
1723
1724
            return 0;
1725
        } else {
1726
            $row = $result->fetchArray(SQLITE3_ASSOC);
1727
1728
            self::$logger->debug('<<getHistoryCount ['.$row['object_count'].']');
1729
1730
            return $row['object_count'];
1731
        }
1732
    }
1733
1734
    /**
1735
     * Given that Enum values are not saved in the database for SQLite, an implementation is not required here.
1736
     *
1737
     * (non-PHPdoc)
1738
     *
1739
     * @see Alpha\Model\ActiveRecordProviderInterface::setEnumOptions()
1740
     *
1741
     * @throws Alpha\Exception\NotImplementedException
1742
     */
1743
    public function setEnumOptions()
1744
    {
1745
        throw new NotImplementedException('ActiveRecordProviderInterface::setEnumOptions() not implemented by the SQLite3 provider');
1746
    }
1747
1748
    /**
1749
     * (non-PHPdoc).
1750
     *
1751
     * @see Alpha\Model\ActiveRecordProviderInterface::checkTableExists()
1752
     */
1753 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...
1754
    {
1755
        self::$logger->debug('>>checkTableExists(checkHistoryTable=['.$checkHistoryTable.'])');
1756
1757
        $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...
1758
1759
        $tableExists = false;
1760
1761
        $sqlQuery = 'SELECT name FROM sqlite_master WHERE type = "table";';
1762
        $this->BO->setLastQuery($sqlQuery);
1763
1764
        $result = self::getConnection()->query($sqlQuery);
1765
1766
        $tableName = ($checkHistoryTable ? $this->BO->getTableName().'_history' : $this->BO->getTableName());
1767
1768
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1769
            if (strtolower($row['name']) == mb_strtolower($tableName)) {
1770
                $tableExists = true;
1771
            }
1772
        }
1773
1774
        if ($result) {
1775
            self::$logger->debug('<<checkTableExists ['.$tableExists.']');
1776
1777
            return $tableExists;
1778
        } else {
1779
            throw new AlphaException('Failed to access the system database correctly, error is ['.self::getLastDatabaseError().']');
1780
            self::$logger->debug('<<checkTableExists [false]');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<...kTableExists [false]'); 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...
1781
1782
            return false;
1783
        }
1784
    }
1785
1786
    /**
1787
     * (non-PHPdoc).
1788
     *
1789
     * @see Alpha\Model\ActiveRecordProviderInterface::checkBOTableExists()
1790
     */
1791 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...
1792
    {
1793
        if (self::$logger == null) {
1794
            self::$logger = new Logger('ActiveRecordProviderSQLite');
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Alpha\Util\Logging\...eRecordProviderSQLite') of type object<Alpha\Util\Logging\Logger> is incompatible with the declared type object<Alpha\Model\Alpha\Util\Logging\Logger> of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1795
        }
1796
        self::$logger->debug('>>checkBOTableExists(BOClassName=['.$BOClassName.'], checkHistoryTable=['.$checkHistoryTable.'])');
1797
1798
        if (!class_exists($BOClassName)) {
1799
            throw new IllegalArguementException('The classname provided ['.$checkHistoryTable.'] is not defined!');
1800
        }
1801
1802
        $tableName = $BOClassName::TABLE_NAME;
1803
1804
        if (empty($tableName)) {
1805
            $tableName = mb_substr($BOClassName, 0, mb_strpos($BOClassName, '_'));
1806
        }
1807
1808
        if ($checkHistoryTable) {
1809
            $tableName .= '_history';
1810
        }
1811
1812
        $tableExists = false;
1813
1814
        $sqlQuery = 'SELECT name FROM sqlite_master WHERE type = "table";';
1815
1816
        $result = self::getConnection()->query($sqlQuery);
1817
1818
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1819
            if ($row['name'] == $tableName) {
1820
                $tableExists = true;
1821
            }
1822
        }
1823
1824
        if ($result) {
1825
            self::$logger->debug('<<checkBOTableExists ['.($tableExists ? 'true' : 'false').']');
1826
1827
            return $tableExists;
1828
        } else {
1829
            throw new AlphaException('Failed to access the system database correctly, error is ['.self::getLastDatabaseError().']');
1830
            self::$logger->debug('<<checkBOTableExists [false]');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<...OTableExists [false]'); 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...
1831
1832
            return false;
1833
        }
1834
    }
1835
1836
    /**
1837
     * (non-PHPdoc).
1838
     *
1839
     * @see Alpha\Model\ActiveRecordProviderInterface::checkTableNeedsUpdate()
1840
     */
1841
    public function checkTableNeedsUpdate()
1842
    {
1843
        self::$logger->debug('>>checkTableNeedsUpdate()');
1844
1845
        if (!$this->BO->checkTableExists()) {
1846
            return false;
1847
        }
1848
1849
        $updateRequired = false;
1850
1851
        $matchCount = 0;
1852
1853
        $query = 'PRAGMA table_info('.$this->BO->getTableName().')';
1854
        $result = self::getConnection()->query($query);
1855
        $this->BO->setLastQuery($query);
1856
1857
        // get the class attributes
1858
        $reflection = new ReflectionClass(get_class($this->BO));
1859
        $properties = $reflection->getProperties();
1860
1861 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...
1862
            $propName = $propObj->name;
1863
            if (!in_array($propName, $this->BO->getTransientAttributes())) {
1864
                $foundMatch = false;
1865
1866
                while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1867
                    if ($propName == $row['name']) {
1868
                        $foundMatch = true;
1869
                        break;
1870
                    }
1871
                }
1872
1873
                if (!$foundMatch) {
1874
                    --$matchCount;
1875
                }
1876
1877
                $result->reset();
1878
            }
1879
        }
1880
1881
        // check for the "classname" field in overloaded tables
1882
        if ($this->BO->isTableOverloaded()) {
1883
            $foundMatch = false;
1884
1885
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1886
                if ('classname' == $row['name']) {
1887
                    $foundMatch = true;
1888
                    break;
1889
                }
1890
            }
1891
            if (!$foundMatch) {
1892
                --$matchCount;
1893
            }
1894
        }
1895
1896
        if ($matchCount != 0) {
1897
            $updateRequired = true;
1898
        }
1899
1900 View Code Duplication
        if (!$result) {
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...
1901
            throw new AlphaException('Failed to access the system database correctly, error is ['.self::getLastDatabaseError().']');
1902
            self::$logger->debug('<<checkTableNeedsUpdate [false]');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<...eNeedsUpdate [false]'); 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...
1903
1904
            return false;
1905
        } else {
1906
            // check the table indexes
1907
            try {
1908
                $this->checkIndexes();
1909
            } catch (AlphaException $ae) {
1910
                self::$logger->warn("Error while checking database indexes:\n\n".$ae->getMessage());
1911
            }
1912
1913
            self::$logger->debug('<<checkTableNeedsUpdate ['.$updateRequired.']');
1914
1915
            return $updateRequired;
1916
        }
1917
    }
1918
1919
    /**
1920
     * (non-PHPdoc).
1921
     *
1922
     * @see Alpha\Model\ActiveRecordProviderInterface::findMissingFields()
1923
     */
1924 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...
1925
    {
1926
        self::$logger->debug('>>findMissingFields()');
1927
1928
        $missingFields = array();
1929
        $matchCount = 0;
1930
1931
        $sqlQuery = 'PRAGMA table_info('.$this->BO->getTableName().')';
1932
        $result = self::getConnection()->query($sqlQuery);
1933
        $this->BO->setLastQuery($sqlQuery);
1934
1935
        // get the class attributes
1936
        $reflection = new ReflectionClass(get_class($this->BO));
1937
        $properties = $reflection->getProperties();
1938
1939
        foreach ($properties as $propObj) {
1940
            $propName = $propObj->name;
1941
            if (!in_array($propName, $this->BO->getTransientAttributes())) {
1942
                while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1943
                    if ($propName == $row['name']) {
1944
                        ++$matchCount;
1945
                        break;
1946
                    }
1947
                }
1948
                $result->reset();
1949
            } else {
1950
                ++$matchCount;
1951
            }
1952
1953
            if ($matchCount == 0) {
1954
                array_push($missingFields, $propName);
1955
            } else {
1956
                $matchCount = 0;
1957
            }
1958
        }
1959
1960
        // check for the "classname" field in overloaded tables
1961
        if ($this->BO->isTableOverloaded()) {
1962
            $foundMatch = false;
1963
1964
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
1965
                if ('classname' == $row['name']) {
1966
                    $foundMatch = true;
1967
                    break;
1968
                }
1969
            }
1970
            if (!$foundMatch) {
1971
                array_push($missingFields, 'classname');
1972
            }
1973
        }
1974
1975
        if (!$result) {
1976
            throw new AlphaException('Failed to access the system database correctly, error is ['.self::getLastDatabaseError().']');
1977
        }
1978
1979
        self::$logger->debug('<<findMissingFields ['.var_export($missingFields, true).']');
1980
1981
        return $missingFields;
1982
    }
1983
1984
    /**
1985
     * (non-PHPdoc).
1986
     *
1987
     * @see Alpha\Model\ActiveRecordProviderInterface::getIndexes()
1988
     */
1989
    public function getIndexes()
1990
    {
1991
        self::$logger->debug('>>getIndexes()');
1992
1993
        $sqlQuery = "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='".$this->BO->getTableName()."'";
1994
1995
        $this->BO->setLastQuery($sqlQuery);
1996
1997
        $indexNames = array();
1998
1999
        if (!$result = self::getConnection()->query($sqlQuery)) {
2000
            throw new AlphaException('Failed to access the system database correctly, error is ['.self::getLastDatabaseError().']');
2001
        } else {
2002
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
2003
                array_push($indexNames, $row['name']);
2004
            }
2005
        }
2006
2007
        // in SQLite foreign keys are not stored in sqlite_master, so we have to run a different query and append the results
2008
        $sqlQuery = 'PRAGMA foreign_key_list('.$this->BO->getTableName().')';
2009
        
2010
        $this->BO->setLastQuery($sqlQuery);
2011
2012
        if (!$result = self::getConnection()->query($sqlQuery)) {
2013
            self::$logger->warn('Error during pragma table foreign key lookup ['.self::getLastDatabaseError().']');
2014
        } else {
2015
            while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
2016
                // SQLite does not name FK indexes, so we will return a fake name based the same convention used in MySQL
2017
                $fakeIndexName = $this->BO->getTableName().'_'.$row['from'].'_fk_idx';
2018
                array_push($indexNames, $fakeIndexName);
2019
            }
2020
        }
2021
2022
        self::$logger->debug('<<getIndexes');
2023
2024
        return $indexNames;
2025
    }
2026
2027
    /**
2028
     * Checks to see if all of the indexes are in place for the BO's table, creates those that are missing.
2029
     *
2030
     * @since 1.2
2031
     */
2032
    private function checkIndexes()
2033
    {
2034
        self::$logger->debug('>>checkIndexes()');
2035
2036
        $indexNames = $this->BO->getIndexes();
2037
2038
        // process unique keys
2039 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...
2040
            // check for composite indexes
2041
            if (mb_strpos($prop, '+')) {
2042
                $attributes = explode('+', $prop);
2043
2044
                $index_exists = false;
2045
                foreach ($indexNames as $index) {
2046
                    if ($attributes[0].'_'.$attributes[1].'_unq_idx' == $index) {
2047
                        $index_exists = true;
2048
                    }
2049
                    if (count($attributes) == 3) {
2050
                        if ($attributes[0].'_'.$attributes[1].'_'.$attributes[2].'_unq_idx' == $index) {
2051
                            $index_exists = true;
2052
                        }
2053
                    }
2054
                }
2055
2056
                if (!$index_exists) {
2057
                    if (count($attributes) == 3) {
2058
                        $this->BO->createUniqueIndex($attributes[0], $attributes[1], $attributes[2]);
2059
                    } else {
2060
                        $this->BO->createUniqueIndex($attributes[0], $attributes[1]);
2061
                    }
2062
                }
2063
            } else {
2064
                $index_exists = false;
2065
                foreach ($indexNames as $index) {
2066
                    if ($prop.'_unq_idx' == $index) {
2067
                        $index_exists = true;
2068
                    }
2069
                }
2070
2071
                if (!$index_exists) {
2072
                    $this->createUniqueIndex($prop);
2073
                }
2074
            }
2075
        }
2076
2077
        self::$logger->debug('<<checkIndexes');
2078
    }
2079
2080
    /**
2081
     * Note that SQLite 3.6.19 is requrired for foreign key support.
2082
     *
2083
     * (non-PHPdoc)
2084
     *
2085
     * @see Alpha\Model\ActiveRecordProviderInterface::createForeignIndex()
2086
     */
2087
    public function createForeignIndex($attributeName, $relatedClass, $relatedClassAttribute, $indexName = null)
2088
    {
2089
        self::$logger->info('>>createForeignIndex(attributeName=['.$attributeName.'], relatedClass=['.$relatedClass.'], relatedClassAttribute=['.$relatedClassAttribute.'], indexName=['.$indexName.']');
2090
2091
        /*
2092
         * High-level approach
2093
         *
2094
         * 1. Rename the source table to [tablename]_temp
2095
         * 2. Create a new [tablename] table, with the new FK in place.
2096
         * 3. Copy all of the data from [tablename]_temp to [tablename].
2097
         * 4. Drop [tablename]_temp.
2098
         */
2099
        try {
2100
            ActiveRecord::begin($this->BO);
2101
2102
            // rename the table to [tablename]_temp
2103
            $query = 'ALTER TABLE '.$this->BO->getTableName().' RENAME TO '.$this->BO->getTableName().'_temp;';
2104
            $this->BO->setLastQuery($query);
2105
            self::getConnection()->query($query);
2106
2107
            self::$logger->info('Renamed the table ['.$this->BO->getTableName().'] to ['.$this->BO->getTableName().'_temp]');
2108
2109
            // now create the new table with the FK in place
2110
            $record = new $relatedClass();
2111
            $tableName = $record->getTableName();
2112
            $this->foreignKeys[$attributeName] = array($tableName, $relatedClassAttribute);
2113
2114
            $this->makeTable();
2115
2116
            self::$logger->info('Made a new copy of the table ['.$this->BO->getTableName().']');
2117
2118
            // copy all of the old data to the new table
2119
            $query = 'INSERT INTO '.$this->BO->getTableName().' SELECT * FROM '.$this->BO->getTableName().'_temp;';
2120
            $this->BO->setLastQuery($query);
2121
            self::getConnection()->query($query);
2122
2123
            self::$logger->info('Copied all of the data from ['.$this->BO->getTableName().'] to ['.$this->BO->getTableName().'_temp]');
2124
2125
            // finally, drop the _temp table and commit the changes
2126
            $this->BO->dropTable($this->BO->getTableName().'_temp');
2127
2128
            self::$logger->info('Dropped the table ['.$this->BO->getTableName().'_temp]');
2129
2130
            ActiveRecord::commit($this->BO);
2131
        } catch (Exception $e) {
2132
            ActiveRecord::rollback($this->BO);
2133
2134
            throw new FailedIndexCreateException('Failed to create the index ['.$attributeName.'] on ['.$this->BO->getTableName().'], error is ['.$e->getMessage().'], query ['.$this->BO->getLastQuery().']');
2135
        }
2136
2137
        self::$logger->info('<<createForeignIndex');
2138
    }
2139
2140
    /**
2141
     * (non-PHPdoc).
2142
     *
2143
     * @see Alpha\Model\ActiveRecordProviderInterface::createUniqueIndex()
2144
     */
2145 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...
2146
    {
2147
        self::$logger->debug('>>createUniqueIndex(attribute1Name=['.$attribute1Name.'], attribute2Name=['.$attribute2Name.'], attribute3Name=['.$attribute3Name.'])');
2148
2149
        if ($attribute2Name != '' && $attribute3Name != '') {
2150
            $sqlQuery = 'CREATE UNIQUE INDEX IF NOT EXISTS '.$attribute1Name.'_'.$attribute2Name.'_'.$attribute3Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.','.$attribute3Name.');';
2151
        }
2152
2153
        if ($attribute2Name != '' && $attribute3Name == '') {
2154
            $sqlQuery = 'CREATE UNIQUE INDEX IF NOT EXISTS '.$attribute1Name.'_'.$attribute2Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.');';
2155
        }
2156
2157
        if ($attribute2Name == '' && $attribute3Name == '') {
2158
            $sqlQuery = 'CREATE UNIQUE INDEX IF NOT EXISTS '.$attribute1Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.');';
2159
        }
2160
2161
        $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...
2162
2163
        $result = self::getConnection()->query($sqlQuery);
2164
2165
        if ($result) {
2166
            self::$logger->debug('Successfully created the unique index on ['.$this->BO->getTableName().']');
2167
        } else {
2168
            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...
2169
        }
2170
2171
        self::$logger->debug('<<createUniqueIndex');
2172
    }
2173
2174
    /**
2175
     * (non-PHPdoc).
2176
     *
2177
     * @see Alpha\Model\ActiveRecordProviderInterface::reload()
2178
     */
2179 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...
2180
    {
2181
        self::$logger->debug('>>reload()');
2182
2183
        if (!$this->BO->isTransient()) {
2184
            $this->BO->load($this->BO->getOID());
2185
        } else {
2186
            throw new AlphaException('Cannot reload transient object from database!');
2187
        }
2188
2189
        self::$logger->debug('<<reload');
2190
    }
2191
2192
    /**
2193
     * (non-PHPdoc).
2194
     *
2195
     * @see Alpha\Model\ActiveRecordProviderInterface::checkRecordExists()
2196
     */
2197
    public function checkRecordExists($OID)
2198
    {
2199
        self::$logger->debug('>>checkRecordExists(OID=['.$OID.'])');
2200
2201
        $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE OID = :OID;';
2202
        $this->BO->setLastQuery($sqlQuery);
2203
        $stmt = self::getConnection()->prepare($sqlQuery);
2204
2205
        $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...
2206
2207
        if ($stmt instanceof SQLite3Stmt) {
2208
            $stmt->bindValue(':OID', $OID, SQLITE3_INTEGER);
2209
2210
            $result = $stmt->execute();
2211
2212
            // there should only ever be one (or none)
2213
            $row = $result->fetchArray(SQLITE3_ASSOC);
2214
2215
            $stmt->close();
2216 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...
2217
            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().']');
2218
            self::$logger->debug('<<checkRecordExists [false]');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<...RecordExists [false]'); 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...
2219
2220
            return false;
2221
        }
2222
2223 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...
2224
            self::$logger->debug('<<checkRecordExists [false]');
2225
2226
            return false;
2227
        } else {
2228
            self::$logger->debug('<<checkRecordExists [true]');
2229
2230
            return true;
2231
        }
2232
    }
2233
2234
    /**
2235
     * (non-PHPdoc).
2236
     *
2237
     * @see Alpha\Model\ActiveRecordProviderInterface::isTableOverloaded()
2238
     */
2239 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...
2240
    {
2241
        self::$logger->debug('>>isTableOverloaded()');
2242
2243
        $reflection = new ReflectionClass($this->BO);
2244
        $classname = $reflection->getShortName();
2245
        $tablename = ucfirst($this->BO->getTableName());
2246
2247
        // use reflection to check to see if we are dealing with a persistent type (e.g. DEnum) which are never overloaded
2248
        $implementedInterfaces = $reflection->getInterfaces();
2249
2250
        foreach ($implementedInterfaces as $interface) {
2251
            if ($interface->name == 'Alpha\Model\Type\TypeInterface') {
2252
                self::$logger->debug('<<isTableOverloaded [false]');
2253
2254
                return false;
2255
            }
2256
        }
2257
2258
        if ($classname != $tablename) {
2259
            // loop over all BOs to see if there is one using the same table as this BO
2260
2261
            $BOclasses = ActiveRecord::getBOClassNames();
2262
2263
            foreach ($BOclasses as $BOclassName) {
2264
                $reflection = new ReflectionClass($BOclassName);
2265
                $classname = $reflection->getShortName();
2266
                if ($tablename == $classname) {
2267
                    self::$logger->debug('<<isTableOverloaded [true]');
2268
2269
                    return true;
2270
                }
2271
            }
2272
            throw new BadTableNameException('The table name ['.$tablename.'] for the class ['.$classname.'] is invalid as it does not match a BO definition in the system!');
2273
            self::$logger->debug('<<isTableOverloaded [false]');
0 ignored issues
show
Unused Code introduced by
self::$logger->debug('<<...leOverloaded [false]'); 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...
2274
2275
            return false;
2276
        } else {
2277
            // check to see if there is already a "classname" column in the database for this BO
2278
            $sqlQuery = 'PRAGMA table_info('.$this->BO->getTableName().')';
2279
            $result = self::getConnection()->query($sqlQuery);
2280
            $this->BO->setLastQuery($sqlQuery);
2281
2282
            if (!$result) {
2283
                self::$logger->warn('Error during pragma table info lookup ['.self::getLastDatabaseError().']');
2284
            } else {
2285
                while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
2286
                    if ('classname' == $row['name']) {
2287
                        self::$logger->debug('<<isTableOverloaded [true]');
2288
2289
                        return true;
2290
                    }
2291
                }
2292
            }
2293
2294
            self::$logger->debug('<<isTableOverloaded [false]');
2295
2296
            return false;
2297
        }
2298
    }
2299
2300
    /**
2301
     * (non-PHPdoc).
2302
     *
2303
     * @see Alpha\Model\ActiveRecordProviderInterface::begin()
2304
     */
2305 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...
2306
    {
2307
        if (self::$logger == null) {
2308
            self::$logger = new Logger('ActiveRecordProviderSQLite');
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Alpha\Util\Logging\...eRecordProviderSQLite') of type object<Alpha\Util\Logging\Logger> is incompatible with the declared type object<Alpha\Model\Alpha\Util\Logging\Logger> of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
2309
        }
2310
        self::$logger->debug('>>begin()');
2311
2312
        if (!self::getConnection()->exec('BEGIN')) {
2313
            throw new AlphaException('Error beginning a new transaction, error is ['.self::getLastDatabaseError().']');
2314
        }
2315
2316
        self::$logger->debug('<<begin');
2317
    }
2318
2319
    /**
2320
     * (non-PHPdoc).
2321
     *
2322
     * @see Alpha\Model\ActiveRecordProviderInterface::commit()
2323
     */
2324 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...
2325
    {
2326
        if (self::$logger == null) {
2327
            self::$logger = new Logger('ActiveRecordProviderSQLite');
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Alpha\Util\Logging\...eRecordProviderSQLite') of type object<Alpha\Util\Logging\Logger> is incompatible with the declared type object<Alpha\Model\Alpha\Util\Logging\Logger> of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
2328
        }
2329
        self::$logger->debug('>>commit()');
2330
2331
        if (!self::getConnection()->exec('COMMIT')) {
2332
            throw new AlphaException('Error commiting a transaction, error is ['.self::getLastDatabaseError().']');
2333
        }
2334
2335
        self::$logger->debug('<<commit');
2336
    }
2337
2338
    /**
2339
     * (non-PHPdoc).
2340
     *
2341
     * @see Alpha\Model\ActiveRecordProviderInterface::rollback()
2342
     */
2343
    public static function rollback()
2344
    {
2345
        if (self::$logger == null) {
2346
            self::$logger = new Logger('ActiveRecordProviderSQLite');
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Alpha\Util\Logging\...eRecordProviderSQLite') of type object<Alpha\Util\Logging\Logger> is incompatible with the declared type object<Alpha\Model\Alpha\Util\Logging\Logger> of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
2347
        }
2348
2349
        self::$logger->debug('>>rollback()');
2350
2351
        try {
2352
            self::getConnection()->exec('ROLLBACK');
2353
            self::disconnect();
2354
        } catch (Exception $e) {
2355
            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
2356
                throw new AlphaException('Error rolling back a transaction, error is ['.self::getLastDatabaseError().']');
2357
            }
2358
        }
2359
2360
        self::$logger->debug('<<rollback');
2361
    }
2362
2363
    /**
2364
     * (non-PHPdoc).
2365
     *
2366
     * @see Alpha\Model\ActiveRecordProviderInterface::setBO()
2367
     */
2368
    public function setBO($BO)
2369
    {
2370
        $this->BO = $BO;
2371
    }
2372
2373
    /**
2374
     * (non-PHPdoc).
2375
     *
2376
     * @see Alpha\Model\ActiveRecordProviderInterface::checkDatabaseExists()
2377
     */
2378
    public static function checkDatabaseExists()
2379
    {
2380
        $config = ConfigProvider::getInstance();
2381
2382
        return file_exists($config->get('db.file.path'));
2383
    }
2384
2385
    /**
2386
     * (non-PHPdoc).
2387
     *
2388
     * @see Alpha\Model\ActiveRecordProviderInterface::createDatabase()
2389
     */
2390
    public static function createDatabase()
2391
    {
2392
        $config = ConfigProvider::getInstance();
2393
2394
        if (!self::checkDatabaseExists()) {
2395
            fopen($config->get('db.file.path'), 'x+');
2396
        }
2397
    }
2398
2399
    /**
2400
     * (non-PHPdoc).
2401
     *
2402
     * @see Alpha\Model\ActiveRecordProviderInterface::dropDatabase()
2403
     */
2404
    public static function dropDatabase()
2405
    {
2406
        $config = ConfigProvider::getInstance();
2407
2408
        if (self::checkDatabaseExists()) {
2409
            unlink($config->get('db.file.path'));
2410
        }
2411
    }
2412
}
2413