Issues (4)

Security Analysis    no request data  

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/MemoryDatabase.php (2 issues)

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
namespace DAL;
3
4
use \DAL\Exceptions\DatabaseException;
5
6
/**
7
 * A database that stores all of its data in the memory.
8
 * Still a work in progress.
9
 */
10
class MemoryDatabase extends AbstractDatabase
11
{
12
13
    public $tables = [];
14
    private $lastIdCreated;
15
    private $lastAutoId = 0;
16
    private $primaryKey;
17
18
    /**
19
     * @param string     $primaryKey
20
     * @param array|null $rawData
21
     */
22 13
    public function __construct($primaryKey = 'id', array $rawData = null)
23
    {
24 13
        $this->primaryKey = $primaryKey;
25 13
        if ($rawData !== null) {
26 13
            $this->tables = $rawData;
27 13
        }
28 13
        parent::__construct();
29
    }
30 4
31
    public function dumpRawData()
32 4
    {
33
        return $this->tables;
34
    }
35
36
    /**
37
     * @return mixed
38 2
     */
39
    public function lastIdCreated()
40 2
    {
41
        return $this->lastIdCreated;
42
    }
43 1
44
    private function createTableIfNotExists($table)
45 1
    {
46
        if (!isset($this->tables[$table])) {
47
            $this->tables[$table] = [];
48 1
        }
49
    }
50
51
    /**
52
     * @param string $table   The table that will be accessed and written.
53
     * @param array  $fields  A list of fields and values to be used when creating a new record.
54
     * @param array  $options The list of options that will help with creating the records.
55
     * @return void
56 1
     */
57
    public function create($table, array $fields, array $options = [])
58 1
    {
59 1
        $this->createTableIfNotExists($table);
60 1
        $primaryKey = $this->primaryKey;
61
        if (isset($fields[$primaryKey])) {
62
            // We will use the primary key already given.
63
            $id = $fields[$primaryKey];
64
        } else {
65 1
            // We have to make our own primary key.
66
            $id = $this->lastAutoId;
67 1
            do {
68 1
                $id++;
69 1
            } while (isset($this->tables[$table][$id]));
70 1
            $this->lastAutoId    = $id;
71
            $fields[$primaryKey] = $id;
72 1
        }
73 1
        $this->lastIdCreated       = $id;
74 1
        $this->tables[$table][$id] = $fields;
75
    }
76
77
    /**
78
     * If the condition matches the data in the record, then the return value is true. Otherwise,
79
     * the return value is false.
80
     *
81
     * @return bool Returns true if the condition matches the data in the record.
82 6
     */
83
    private function isRecordMatchingCondition($primaryKeyValue, $record, Condition $condition = null)
84 6
    {
85 6
        if ($condition === null) {
86
            return true;
87
        }
88
        $operator = $condition->getOperator();
89
        $field    = $condition->getLeft();
90
        $value    = $condition->getRight();
91 View Code Duplication
        if ($operator === 'and') {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
92
            return $this->isRecordMatchingCondition($primaryKeyValue, $record, $field)
93
                && $this->isRecordMatchingCondition($primaryKeyValue, $record, $value);
94
        }
95 View Code Duplication
        if ($operator === 'or') {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
96
            return $this->isRecordMatchingCondition($primaryKeyValue, $record, $field)
97
                || $this->isRecordMatchingCondition($primaryKeyValue, $record, $value);
98
        }
99
        if ($field === $this->primaryKey) {
100
            $actualValue = $primaryKeyValue;
101
        } elseif (isset($record[$field])) {
102
            $actualValue = $record[$field];
103
        } else {
104
            $actualValue = null;
105
        }
106
        if ($operator === '=') {
107
            // A weak comparison is done intentionally, as some SQL databases
108
            // use weak comparisons.
109
            return $actualValue == $value;
110
        }
111
        if ($operator === '!=') {
112
            return $actualValue != $value;
113
        }
114
        if ($operator === '>') {
115
            return $actualValue > $value;
116
        }
117
        if ($operator === '<') {
118
            return $actualValue < $value;
119
        }
120
        if ($operator === '>=') {
121
            return $actualValue >= $value;
122
        }
123
        if ($operator === '<=') {
124
            return $actualValue <= $value;
125
        }
126
        if ($operator === 'regex') {
127
            $result = preg_match($value, $actualValue);
128
            if ($result === false) {
129
                $message = 'The regular expression provided is invalid! Expression: ' . $value;
130
                throw new DatabaseException($message);
131
            }
132
            return $result === 1;
133
        }
134
        $message = 'Invalid operation! You must use one of the provided static methods!';
135
        throw new DatabaseException($message);
136
    }
137
138
    /**
139
     * @param string         $table    The table that will be accessed and written.
140
     * @param array          $fields   A list of fields and values to be used when updating a record.
141
     * @param Condition|null $criteria The criteria that will filter the data.
142
     * @param array          $options  The list of options that will help with updating the records.
143
     * @return void
144 1
     */
145
    public function update($table, array $fields, Condition $criteria = null, array $options = [])
146 1
    {
147
        if (isset($fields[$this->primaryKey])) {
148
            throw new DatabaseException(
149
                'Error when updating a record! You may NOT change the primary key of a record!'
150
            );
151
        }
152
153
        // The code below is included for increased efficiency.
154
        // It will check to see if a primary key is being compared.
155 1
        if ($criteria != null
156 1
            && $criteria->getOperator() === '='
157 1
            && $criteria->getLeft() === $this->primaryKey
158
        ) {
159
            $primaryKeyValue = $criteria->getRight();
160
            if (isset($this->tables[$table][$primaryKeyValue])) {
161
                foreach ($fields as $key => $value) {
162
                    $this->tables[$table][$primaryKeyValue][$key] = $value;
163
                }
164
            }
165
            return;
166
        }
167
168 1
        foreach ($this->tables[$table] as $primaryKeyValue => $record) {
169 1
            if ($this->isRecordMatchingCondition($primaryKeyValue, $record, $criteria)) {
170 1
                foreach ($fields as $key => $value) {
171 1
                    $this->tables[$table][$primaryKeyValue][$key] = $value;
172
                }
173 1
            }
174 1
        }
175 1
    }
176 1
177 1
    /**
178
     * @param string         $table    The table that will be accessed and written.
179
     * @param Condition|null $criteria The criteria that will filter the data.
180
     * @param array          $options  The list of options that will help with deleting the records.
181
     * @return void
182
     */
183
    public function delete($table, Condition $criteria = null, array $options = [])
184
    {
185 1
        if (!isset($this->tables[$table])) {
186
            return;
187 1
        }
188
189
        if ($criteria === null) {
190
            $this->tables[$table] = [];
191 1
            return;
192 1
        }
193 1
194
        // The code below is included for increased efficiency.
195
        // It will check to see if a primary key is being compared.
196
        if ($criteria->getOperator() === '=' && $criteria->getLeft() === $this->primaryKey) {
197
            $primaryKeyValue = $criteria->getRight();
198
            if (isset($this->tables[$table][$primaryKeyValue])) {
199
                unset($this->tables[$table][$primaryKeyValue]);
200
            }
201
            return;
202
        }
203
204
        foreach ($this->tables[$table] as $primaryKeyValue => $record) {
205
            if ($this->isRecordMatchingCondition($primaryKeyValue, $record, $criteria)) {
206
                unset($this->tables[$table][$primaryKeyValue]);
207
            }
208
        }
209
        return;
210
    }
211
212
    /**
213
     * Returns all records that match the criteria.
214
     *
215
     * @param string         $table The table that will be accessed and written.
216
     * @param Condition|null $criteria The criteria that will filter the records.
217
     * @param array          $options The list of options that will help with finding the records.
218
     * @return array[] Multiple records from the table that match the criteria.
219
     */
220
    public function findAll($table, Condition $criteria = null, array $options = [])
221
    {
222 6
        $limit = null;
223
        if (isset($options['limit'])) {
224 6
            $limit = $options['limit'];
225 6
        }
226 4
227 4
        if (($limit !== null && $limit < 1) || !isset($this->tables[$table])) {
228
            return [];
229 6
        }
230 1
231
        // The code below is included for increased efficiency.
232
        // It will check to see if a primary key is being compared.
233
        if ($criteria !== null && $criteria->getOperator() === '=' && $criteria->getLeft() === $this->primaryKey) {
234
            $primaryKeyValue = $criteria->getRight();
235 5
            if (isset($this->tables[$table][$primaryKeyValue])) {
236
                return [$primaryKeyValue => $this->tables[$table][$primaryKeyValue]];
237
            }
238
            return [];
239
        }
240
241
        $count   = 0;
242
        $records = [];
243 5
        foreach ($this->tables[$table] as $primaryKeyValue => $record) {
244 5
            if ($limit !== null && $count >= $limit) {
245 5
                // We have reached our limit!
246 5
                break;
247
            }
248 3
            if ($this->isRecordMatchingCondition($primaryKeyValue, $record, $criteria)) {
249
                $records[$primaryKeyValue] = $record;
250 5
                $count++;
251 5
            }
252 5
        }
253 5
        return $records;
254 5
    }
255 5
256
    /**
257
     * Returns the number of records that match the criteria.
258
     *
259
     * If the crtieria is null, then the return value is the total number of
260
     * records in the table.
261
     *
262
     * @param string         $table The table that will be accessed and written.
263
     * @param Condition|null $criteria The criteria that will filter the records.
264
     * @param array          $options The list of options that help with counting the records.
265
     * @return int The number of records that match the criteria.
266
     */
267
    public function count($table, Condition $criteria = null, array $options = [])
268
    {
269 1
        if (!isset($this->tables[$table])) {
270
            return 0;
271 1
        }
272
273
        if ($criteria === null) {
274
            return count($this->tables[$table]);
275 1
        }
276 1
277
        $count = 0;
278
        foreach ($this->tables[$table] as $primaryKeyValue => $record) {
279
            if ($this->isRecordMatchingCondition($primaryKeyValue, $record, $criteria)) {
280
                $count++;
281
            }
282
        }
283
        return $count;
284
    }
285
286
    /**
287
     * Returns true if there exists a record that matches the criteria.
288
     *
289
     * If the criteria is null and the table has data, then the return value is true. There are two
290
     * reasons for this result.
291
     *  - The table has stuff.
292
     *  - Running `$db->has('table_name')` is similar to saying,
293
          "does the database have table_name?"
294
     *
295
     * @param string         $table The table that will be accessed and written.
296
     * @param Condition|null $criteria The criteria that will filter the records.
297
     * @param array          $options The list of options that will help with finding the records.
298
     * @return boolean A boolean value indicating if any record matches the criteria.
299
     */
300
    public function has($table, Condition $criteria = null, array $options = [])
301
    {
302 1
        if (!isset($this->tables[$table])) {
303
            return false;
304 1
        }
305
306
        if ($criteria === null) {
307
            return !empty($this->tables[$table]);
308 1
        }
309 1
310
        foreach ($this->tables[$table] as $primaryKeyValue => $record) {
311
            if ($this->isRecordMatchingCondition($primaryKeyValue, $record, $criteria)) {
312
                return true;
313
            }
314
        }
315
        return false;
316
    }
317
}
318