Passed
Push — master ( b1fefe...1b2e02 )
by Michael
05:24
created

MemoryDatabase::isRecordMatchingCondition()   C

Complexity

Conditions 16
Paths 32

Size

Total Lines 54
Code Lines 38

Duplication

Lines 8
Ratio 14.81 %

Code Coverage

Tests 3
CRAP Score 216.06

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 8
loc 54
ccs 3
cts 38
cp 0.0789
rs 6.6857
cc 16
eloc 38
nc 32
nop 3
crap 216.06

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
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
     */
21 13
    public function __construct($primaryKey = 'id', $rawData = null)
22
    {
23 13
        $this->primaryKey = $primaryKey;
24 13
        if ($rawData !== null) {
25 13
            $this->tables = $rawData;
26 13
        }
27 13
    }
28
29 4
    public function dumpRawData()
30
    {
31 4
        return $this->tables;
32
    }
33
34
    /**
35
     * @return mixed
36
     */
37 2
    public function lastIdCreated()
38
    {
39 2
        return $this->lastIdCreated;
40
    }
41
42 1
    private function createTableIfNotExists($table)
43
    {
44 1
        if (!isset($this->tables[$table])) {
45
            $this->tables[$table] = [];
46
        }
47 1
    }
48
49
    /**
50
     * @param string     $table
51
     * @param array      $fields
52
     * @param array|null $options
53
     * @return void
54
     */
55 1
    public function create($table, array $fields, array $options = [])
56
    {
57 1
        $this->createTableIfNotExists($table);
58 1
        $primaryKey = $this->primaryKey;
59 1
        if (isset($fields[$primaryKey])) {
60
            // We will use the primary key already given.
61
            $id = $fields[$primaryKey];
62
        } else {
63
            // We have to make our own primary key.
64 1
            $id = $this->lastAutoId;
65
            do {
66 1
                $id++;
67 1
            } while (isset($this->tables[$table][$id]));
68 1
            $this->lastAutoId    = $id;
69 1
            $fields[$primaryKey] = $id;
70
        }
71 1
        $this->lastIdCreated       = $id;
72 1
        $this->tables[$table][$id] = $fields;
73 1
    }
74
75
    /**
76
     * If the condition matches the data in the record, then the return value is true. Otherwise,
77
     * the return value is false.
78
     *
79
     * @return bool Returns true if the condition matches the data in the record.
80
     */
81 6
    private function isRecordMatchingCondition($primaryKeyValue, $record, Condition $condition = null)
82
    {
83 6
        if ($condition === null) {
84 6
            return true;
85
        }
86
        $operator = $condition->getOperator();
87
        $field    = $condition->getLeft();
88
        $value    = $condition->getRight();
89 View Code Duplication
        if ($operator === 'and') {
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...
90
            return $this->isRecordMatchingCondition($primaryKeyValue, $record, $field)
91
                && $this->isRecordMatchingCondition($primaryKeyValue, $record, $value);
92
        }
93 View Code Duplication
        if ($operator === 'or') {
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...
94
            return $this->isRecordMatchingCondition($primaryKeyValue, $record, $field)
95
                || $this->isRecordMatchingCondition($primaryKeyValue, $record, $value);
96
        }
97
        if ($field === $this->primaryKey) {
98
            $actualValue = $primaryKeyValue;
99
        } elseif (isset($record[$field])) {
100
            $actualValue = $record[$field];
101
        } else {
102
            $actualValue = null;
103
        }
104
        if ($operator === '=') {
105
            // A weak comparison is done intentionally, as some SQL databases
106
            // use weak comparisons.
107
            return $actualValue == $value;
108
        }
109
        if ($operator === '!=') {
110
            return $actualValue != $value;
111
        }
112
        if ($operator === '>') {
113
            return $actualValue > $value;
114
        }
115
        if ($operator === '<') {
116
            return $actualValue < $value;
117
        }
118
        if ($operator === '>=') {
119
            return $actualValue >= $value;
120
        }
121
        if ($operator === '<=') {
122
            return $actualValue <= $value;
123
        }
124
        if ($operator === 'regex') {
125
            $result = preg_match($value, $actualValue);
126
            if ($result === false) {
127
                $message = 'The regular expression provided is invalid! Expression: ' . $value;
128
                throw new DatabaseException($message);
129
            }
130
            return $result === 1;
131
        }
132
        $message = 'Invalid operation! You must use one of the provided static methods!';
133
        throw new DatabaseException($message);
134
    }
135
136
    /**
137
     * @param string         $table
138
     * @param array          $fields
139
     * @param Condition|null $criteria
140
     * @param array|null     $options
141
     * @return array An array of records that have been updated.
142
     */
143 1
    public function update($table, array $fields, Condition $criteria = null, array $options = [])
144
    {
145 1
        if (isset($fields[$this->primaryKey])) {
146
            throw new DatabaseException(
147
                'Error when updating a record! You may NOT change the primary key of a record!'
148
            );
149
        }
150
151
        // The code below is included for increased efficiency.
152
        // It will check to see if a primary key is being compared.
153
        if ($criteria != null
154 1
            && $criteria->getOperator() === '='
155 1
            && $criteria->getLeft() === $this->primaryKey
156 1
        ) {
157
            $primaryKeyValue = $criteria->getRight();
158
            if (isset($this->tables[$table][$primaryKeyValue])) {
159
                $record = $this->tables[$table][$primaryKeyValue];
160
                foreach ($fields as $key => $value) {
161
                    $this->tables[$table][$key] = $value;
162
                }
163
                return [$primaryKeyValue => $record];
164
            }
165
            return [];
166
        }
167
168 1
        $records = [];
169 1
        foreach ($this->tables[$table] as $primaryKeyValue => $record) {
170 1
            if ($this->isRecordMatchingCondition($primaryKeyValue, $record, $criteria)) {
171 1
                foreach ($fields as $key => $value) {
172
                    $this->tables[$table][$key] = $value;
173 1
                }
174 1
                $records[$primaryKeyValue] = $record;
175 1
            }
176 1
        }
177 1
        return $records;
178
    }
179
180
    /**
181
     * @param string         $table
182
     * @param Condition|null $criteria
183
     * @param array|null     $options
184
     * @return array An array of records that were deleted.
185
     */
186 1
    public function delete($table, Condition $criteria = null, array $options = [])
187
    {
188 1
        if (!isset($this->tables[$table])) {
189
            return [];
190
        }
191
192 1
        if ($criteria === null) {
193 1
            $tableData            = $this->tables[$table];
194 1
            $this->tables[$table] = [];
195 1
            return $tableData;
196
        }
197
198
        // The code below is included for increased efficiency.
199
        // It will check to see if a primary key is being compared.
200
        if ($criteria->getOperator() === '=' && $criteria->getLeft() === $this->primaryKey) {
201
            $primaryKeyValue = $criteria->getRight();
202
            if (isset($this->tables[$table][$primaryKeyValue])) {
203
                $record = $this->tables[$table][$primaryKeyValue];
204
                unset($this->tables[$table][$primaryKeyValue]);
205
                return [$primaryKeyValue => $record];
206
            }
207
            return [];
208
        }
209
210
        $records = [];
211
        foreach ($this->tables[$table] as $primaryKeyValue => $record) {
212
            if ($this->isRecordMatchingCondition($primaryKeyValue, $record, $criteria)) {
213
                unset($this->tables[$table][$primaryKeyValue]);
214
                $records[$primaryKeyValue] = $record;
215
            }
216
        }
217
        return $records;
218
    }
219
220
    /**
221
     * Returns all records that match the criteria.
222
     *
223
     * @param string         $table
224
     * @param Condition|null $criteria
225
     * @param array|null     $options
226
     * @return array
227
     */
228 6
    public function findAll($table, Condition $criteria = null, array $options = [])
229
    {
230 6
        $limit = null;
231 6
        if (isset($options['limit'])) {
232 4
            $limit = $options['limit'];
233 4
        }
234
235 6
        if (($limit !== null && $limit < 1) || !isset($this->tables[$table])) {
236 1
            return [];
237
        }
238
239
        // The code below is included for increased efficiency.
240
        // It will check to see if a primary key is being compared.
241 5
        if ($criteria !== null && $criteria->getOperator() === '=' && $criteria->getLeft() === $this->primaryKey) {
242
            $primaryKeyValue = $criteria->getRight();
243
            if (isset($this->tables[$table][$primaryKeyValue])) {
244
                return [$primaryKeyValue => $this->tables[$table][$primaryKeyValue]];
245
            }
246
            return [];
247
        }
248
249 5
        $count   = 0;
250 5
        $records = [];
251 5
        foreach ($this->tables[$table] as $primaryKeyValue => $record) {
252 5
            if ($limit !== null && $count >= $limit) {
253
                // We have reached our limit!
254 3
                break;
255
            }
256 5
            if ($this->isRecordMatchingCondition($primaryKeyValue, $record, $criteria)) {
257 5
                $records[$primaryKeyValue] = $record;
258 5
                $count++;
259 5
            }
260 5
        }
261 5
        return $records;
262
    }
263
264
    /**
265
     * Returns the number of records that match the criteria.
266
     *
267
     * If the crtieria is null, then the return value is the total number of
268
     * records in the table.
269
     *
270
     * @param string         $table
271
     * @param Condition|null $criteria
272
     * @param array|null     $options
273
     * @return int
274
     */
275 1
    public function count($table, Condition $criteria = null, array $options = [])
276
    {
277 1
        if (!isset($this->tables[$table])) {
278
            return 0;
279
        }
280
281 1
        if ($criteria === null) {
282 1
            return count($this->tables[$table]);
283
        }
284
285
        $count = 0;
286
        foreach ($this->tables[$table] as $primaryKeyValue => $record) {
287
            if ($this->isRecordMatchingCondition($primaryKeyValue, $record, $criteria)) {
288
                $count++;
289
            }
290
        }
291
        return $count;
292
    }
293
294
    /**
295
     * Returns true if there exists a record that matches the criteria.
296
     *
297
     * If the criteria is null and the table has data, then the return value is true. There are two
298
     * reasons for this result.
299
     *  - The table has stuff.
300
     *  - Running `$db->has('table_name')` is similar to saying,
301
          "does the database have table_name?"
302
     *
303
     * @param string         $table
304
     * @param Condition|null $criteria
305
     * @param array|null     $options
306
     * @return boolean
307
     */
308 1
    public function has($table, Condition $criteria = null, array $options = [])
309
    {
310 1
        if (!isset($this->tables[$table])) {
311
            return false;
312
        }
313
314 1
        if ($criteria === null) {
315 1
            return !empty($this->tables[$table]);
316
        }
317
318
        foreach ($this->tables[$table] as $primaryKeyValue => $record) {
319
            if ($this->isRecordMatchingCondition($primaryKeyValue, $record, $criteria)) {
320
                return true;
321
            }
322
        }
323
        return false;
324
    }
325
}
326