Issues (273)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Model/BaseModel.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file contains the skeleton for all of the database objects
4
 *
5
 * @package    BZiON\Models
6
 * @license    https://github.com/allejo/bzion/blob/master/LICENSE.md GNU General Public License Version 3
7
 */
8
9
/**
10
 * A base database object (e.g. A player or a team)
11
 * @package    BZiON\Models
12
 */
13
abstract class BaseModel implements ModelInterface
14
{
15
    /**
16
     * The Database ID of the object
17
     * @var int
18
     */
19
    protected $id;
20
21
    /**
22
     * The name of the database table used for queries
23
     * @var string
24
     */
25
    protected $table;
26
27
    /**
28
     * False if there isn't any row in the database representing
29
     * the requested object ID
30
     * @var bool
31
     */
32
    protected $valid;
33
34
    /**
35
     * The status of this model in the database; active, deleted, etc.
36
     *
37
     * @var string
38
     */
39
    protected $status;
40
41
    /**
42
     * The database variable used for queries
43
     * @var Database
44
     */
45
    protected $db;
46
47
    /**
48
     * Whether the lazy parameters of the model have been loaded
49
     * @var bool
50
     */
51
    protected $loaded = false;
52
53
    /**
54
     * The default status value for deletable models
55
     */
56
    const DEFAULT_STATUS = 'active';
57
58
    /**
59
     * The name of the database table used for queries
60
     * You can use this constant in static methods as such:
61 78
     * static::TABLE
62
     */
63 78
    const TABLE = "";
64 2
65
    /**
66
     * Get a Model based on its ID
67 77
     *
68
     * @param  int|static $id The ID of the object to look for, or the object
69 1
     *                        itself
70
     * @throws InvalidArgumentException If $id is an object of an incorrect type
71
     * @return static
72 76
     */
73
    public static function get($id)
74 76
    {
75
        if ($id instanceof static) {
76
            return $id;
77
        }
78
79
        if (is_object($id)) {
80
            // Throw an exception if $id is an object of the incorrect class
81
            throw new InvalidArgumentException("The object provided is not of the correct type");
82
        }
83
84
        $id = (int) $id;
85
86
        return static::chooseModelFromDatabase($id);
87
    }
88
89
    /**
90
     * Assign the MySQL result array to the individual properties of the model
91
     *
92
     * @param  array $result MySQL's result array
93
     * @return null
94
     */
95
    abstract protected function assignResult($result);
96
97
    /**
98
     * Fetch the columns of a model
99 76
     *
100
     * This method takes the ID of the object to look for and creates a
101 76
     * $this->db object which can be used to communicate with the database and
102 76
     * calls $this->assignResult() so that the child class can populate the
103
     * properties of the Model based on the database data
104 76
     *
105 76
     * If the $id is specified as 0, then an invalid object will be returned
106
     *
107 76
     * @param int $id The ID of the model
108
     * @param array|null $results The column values of the model, or NULL to
109
     *                            generate them using $this->fetchColumnValues()
110 76
     */
111
    protected function __construct($id, $results = null)
112 76
    {
113 76
        $this->db = Database::getInstance();
114
        $this->table = static::TABLE;
115
116 76
        if ($id == 0) {
117
            $this->valid = false;
118
119 76
            return;
120 76
        }
121
122 76
        $this->id = $id;
123
124
        if ($results == null) {
125
            $results = $this->fetchColumnValues($id);
126
        }
127
128
        if ($results === null) {
129
            $this->valid = false;
130
        } else {
131
            $this->valid = true;
132 76
            $this->assignResult($results);
133
        }
134 76
    }
135 76
136
    /**
137
     * Update a database field
138
     *
139
     * @param string $name  The name of the column
140
     * @param mixed  $value The value to set the column to
141
     *
142
     * @return void
143
     */
144 3
    public function update($name, $value)
145
    {
146 3
        $this->db->execute("UPDATE " . static::TABLE . " SET `$name` = ? WHERE id = ?", array($value, $this->id));
147 3
    }
148 3
149
    /**
150
     * Delete the object
151
     *
152
     * Please note that this does not delete the object entirely from the database,
153 76
     * it only hides it from users. You should overload this function if your object
154
     * does not have a 'status' column which can be set to 'deleted'.
155 76
     */
156 76
    public function delete()
157
    {
158
        $this->status = 'deleted';
159
        $this->update('status', 'deleted');
160
    }
161
162 76
    /**
163
     * Permanently delete the object from the database
164 76
     */
165
    public function wipe()
166
    {
167
        $this->db->execute("DELETE FROM " . static::TABLE . " WHERE id = ?", array($this->id));
168
    }
169
170
    /**
171 76
     * If a model has been marked as deleted in the database, this'll go through the process of marking the model
172
     * "active" again.
173 76
     */
174
    public function restore()
175
    {
176
        $this->status = static::DEFAULT_STATUS;
177
        $this->update('status', static::DEFAULT_STATUS);
178
    }
179
180
    /**
181
     * Get an object's database ID
182 76
     * @return int The ID
183
     */
184 76
    public function getId()
185
    {
186
        return $this->id;
187
    }
188
189
    /**
190
     * See if an object is valid
191
     * @return bool
192
     */
193 76
    public function isValid()
194
    {
195 76
        return $this->valid;
196 76
    }
197
198 76
    /**
199 76
     * Fetch a model based on its ID, useful for abstract model classes
200
     *
201 76
     * @param int $id The ID of the model
202
     * @return Model
203
     */
204
    protected static function chooseModelFromDatabase($id)
205 76
    {
206
        return new static($id);
207
    }
208
209
    /**
210
     * Query the database to get the eager column values for the Model
211
     *
212
     * @param $id int The ID of the model to fetch
213
     * @return array|null The results or null if a model wasn't found
214
     */
215
    protected static function fetchColumnValues($id)
216
    {
217 1
        $table = static::TABLE;
218
        $columns = static::getEagerColumns();
219 1
220 1
        $results = Database::getInstance()
221
            ->query("SELECT $columns FROM $table WHERE id = ? LIMIT 1", array($id));
222 1
223 1
        if (count($results) < 1) {
224
            return null;
225
        }
226 1
227
        return $results[0];
228
    }
229
230
    /**
231
     * Counts the elements of the database that match a specific query
232
     *
233
     * @param  string $additional_query The MySQL query string (e.g. `WHERE id = ?`)
234
     * @param  array  $params           The parameter values that will be passed to Database::query()
235 26
     * @param  string $table            The database table that will be searched, defaults to the model's table
236
     * @param  string $column           Only count the entries where `$column` is not `NULL` (or all if `$column` is `*`)
237 26
     * @return int
238
     */
239
    protected static function fetchCount($additional_query = '', $params = array(), $table = '', $column = '*')
240 26
    {
241
        $table = (empty($table)) ? static::TABLE : $table;
242
        $db = Database::getInstance();
243
244
        $result = $db->query("SELECT COUNT($column) AS count FROM $table $additional_query",
245
            $params
246
        );
247
248
        return $result[0]['count'];
249
    }
250
251
    /**
252
     * Gets the id of a database row which has a specific value on a column
253 76
     * @param  string $value  The value which the column should be equal to
254
     * @param  string $column The name of the database column
255 76
     * @return int    The ID of the object
256 76
     */
257
    protected static function fetchIdFrom($value, $column)
258
    {
259 76
        $results = self::fetchIdsFrom($column, $value, false, "LIMIT 1");
260
261
        // Return the id or 0 if nothing was found
262
        return (isset($results[0])) ? $results[0] : 0;
263 76
    }
264
265 76
    /**
266 76
     * Gets an array of object IDs from the database
267
     *
268
     * @param string          $additional_query Additional query snippet passed to the MySQL query after the SELECT statement (e.g. `WHERE id = ?`)
269 76
     * @param array           $params           The parameter values that will be passed to Database::query()
270
     * @param string          $table            The database table that will be searched
271
     * @param string|string[] $select           The column that will be returned
272
     *
273
     * @return int[] A list of values, if $select was only one column, or the return array of $db->query if it was more
274
     */
275
    protected static function fetchIds($additional_query = '', $params = array(), $table = "", $select = 'id')
276
    {
277
        $table = (empty($table)) ? static::TABLE : $table;
278
        $db = Database::getInstance();
279
280
        // If $select is an array, convert it into a comma-separated list that MySQL will accept
281
        if (is_array($select)) {
282
            $select = implode(",", $select);
283
        }
284 76
285
        $results = $db->query("SELECT $select FROM $table $additional_query", $params);
286 76
287 76
        if (!$results) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $results of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
288
            return array();
289 76
        }
290 29
291
        return array_column($results, 0);
292
    }
293 76
294 76
    /**
295
     * Gets an array of object IDs from the database that have a column equal to something else
296
     *
297 76
     * @param string          $column           The name of the column that should be tested
298
     * @param array|mixed     $possible_values  List of acceptable values
299
     * @param bool            $negate           Whether to search if the value of $column does NOT belong to the $possible_values array
300
     * @param string|string[] $select           The name of the column(s) that the returned array should contain
301
     * @param string          $additional_query Additional parameters to be passed to the MySQL query (e.g. `WHERE id = 5`)
302
     * @param string          $table            The database table which will be used for queries
303
     *
304
     * @return int[] A list of values, if $select was only one column, or the return array of $db->query if it was more
305
     */
306 76
    protected static function fetchIdsFrom($column, $possible_values, $negate = false, $additional_query = "", $table = "", $select = 'id')
307
    {
308
        $question_marks = array();
309 76
        $negation = ($negate) ? "NOT" : "";
310
311
        if (!is_array($possible_values)) {
312
            $possible_values = array($possible_values);
313
        }
314
315
        foreach ($possible_values as $p) {
316
            $question_marks[] = '?';
317
        }
318
319
        if (empty($possible_values)) {
320
            if (!$negate) {
321 72
                // There isn't any value that $column can have so
322
                // that it matches the criteria - return nothing.
323 72
                return array();
324
            } else {
325
                $conditionString = $additional_query;
326
            }
327
        } else {
328
            $conditionString = "WHERE $column $negation IN (" . implode(",", $question_marks) . ") $additional_query";
329
        }
330
331
        return self::fetchIds($conditionString, $possible_values, $table, $select);
332
    }
333
334
    /**
335
     * Get the MySQL columns that will be loaded as soon as the model is created
336
     *
337
     * @todo Make this protected
338
     *
339
     * @param string $prefix The prefix that'll be prefixed to column names
340
     *
341
     * @return string The columns in a format readable by MySQL
342
     */
343
    public static function getEagerColumns($prefix = null)
344
    {
345
        return self::formatColumns($prefix, ['*']);
346
    }
347
348
    /**
349 76
     * Get the MySQL columns that will be loaded only when a corresponding
350
     * parameter of the model is requested
351 76
     *
352 76
     * This is done in order to reduce the time needed to load parameters that
353
     * will not be requested (e.g player activation codes or permissions)
354
     *
355 25
     * @return string|null The columns in a format readable by MySQL or null to
356
     *                     fetch no columns at all
357
     */
358
    protected static function getLazyColumns()
359
    {
360
        throw new Exception("You need to specify a Model::getLazyColumns() method");
361
    }
362
363
    /**
364
     * Get a formatted string with a comma separated column list with table/alias prefixes if necessary.
365
     *
366
     * @param string|null $prefix  The table name or SQL alias to be prepend to these columns
367
     * @param array       $columns The columns to format
368
     *
369
     * @return string
370
     */
371
    protected static function formatColumns($prefix = null, $columns = ['*'])
372
    {
373
        if ($prefix === null) {
374
            return implode(',', $columns);
375
        }
376 76
377
        return (($prefix . '.') . implode(sprintf(',%s.', $prefix), $columns));
378 76
    }
379 76
380
    /**
381 76
     * Load all the parameters of the model that were not loaded during the first
382
     * fetch from the database
383 76
     *
384 76
     * @param  array $result MySQL's result set
385
     * @return void
386 76
     */
387
    protected function assignLazyResult($result)
388
    {
389
        throw new Exception("You need to specify a Model::lazyLoad() method");
390 76
    }
391
392
    /**
393
     * Load all the properties of the model that haven't been loaded yet
394
     *
395
     * @param  bool $force Whether to force a reload
396 76
     * @return self
397
     */
398
    protected function lazyLoad($force = false)
399
    {
400
        if ((!$this->loaded || $force) && $this->valid) {
401
            $this->loaded = true;
402
403
            $columns = $this->getLazyColumns();
404 1
405
            if ($columns !== null) {
406 1
                $results = $this->db->query("SELECT $columns FROM {$this->table} WHERE id = ? LIMIT 1", array($this->id));
407
408
                if (count($results) < 1) {
409
                    throw new Exception("The model has mysteriously disappeared");
410
                }
411
412
                $this->assignLazyResult($results[0]);
413
            } else {
414
                $this->assignLazyResult(array());
415
            }
416
        }
417
418
        return $this;
419
    }
420
421
    /**
422
     * Gets an entity from the supplied slug, which can either be an alias or an ID
423 76
     * @param  string|int $slug The object's slug
424
     * @return static
425 76
     */
426 76
    public static function fetchFromSlug($slug)
427
    {
428 76
        return static::get((int) $slug);
429 76
    }
430
431 76
    /**
432 76
     * Creates a new entry in the database
433
     *
434 76
     * <code>
435 45
     * Model::create(array( 'author'=>15, 'content'=>"Lorem ipsum..."  ));
436
     * </code>
437 42
     *
438
     * @param  array        $params An associative array, with the keys (columns) pointing to the
439
     *                              values you want to put on each
440 45
     * @param  array|string $now    Column(s) to update with the current timestamp
441 45
     * @param  string       $table  The table to perform the query on, defaults to the Model's
442 45
     *                              table
443
     * @return static       The new entry
444
     */
445
    protected static function create($params, $now = null, $table = '')
446 76
    {
447 76
        $table = (empty($table)) ? static::TABLE : $table;
448
        $db = Database::getInstance();
449 76
        $id = $db->insert($table, $params, $now);
450
451
        return static::get($id);
452
    }
453
454
    /**
455
     * Fetch a model's data from the database again
456
     * @return static The new model
457
     */
458
    public function refresh()
459
    {
460
        self::__construct($this->id);
461
462
        if ($this->loaded) {
463
            // Load the lazy parameters of the model if they're loaded already
464
            $this->lazyLoad(true);
465
        }
466
467
        return $this;
468
    }
469
470
    /**
471
     * Generate an invalid object
472
     *
473
     * <code>
474
     *     <?php
475
     *     $object = Team::invalid();
476
     *
477
     *     get_class($object); // Team
478
     *     $object->isValid(); // false
479
     * </code>
480 13
     * @return static
481
     */
482 13
    public static function invalid()
483
    {
484
        return new static(0);
485
    }
486
}
487