Completed
Push — master ( f7755d...fe6ccb )
by Chris
02:53
created

InMemory   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 314
Duplicated Lines 4.46 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 19
Bugs 1 Features 11
Metric Value
wmc 32
c 19
b 1
f 11
lcom 1
cbo 5
dl 14
loc 314
rs 9.6

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A limit() 0 3 2
A read() 0 13 2
B listing() 0 23 5
A count() 7 7 2
A create() 0 9 2
B update() 0 24 3
A delete() 7 7 2
A search() 0 16 4
A distinct() 0 11 2
B execute() 0 37 5
A query() 0 4 1
A error() 0 3 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
namespace Darya\Storage;
3
4
use Darya\Storage\Aggregational;
5
use Darya\Storage\Filterer;
6
use Darya\Storage\Readable;
7
use Darya\Storage\Modifiable;
8
use Darya\Storage\Query;
9
use Darya\Storage\Queryable;
10
use Darya\Storage\Result;
11
use Darya\Storage\Searchable;
12
use Darya\Storage\Sorter;
13
14
/**
15
 * Darya's in-memory storage interface.
16
 * 
17
 * Useful for unit testing!
18
 * 
19
 * @author Chris Andrew <[email protected]>
20
 */
21
class InMemory implements Readable, Modifiable, Searchable, Aggregational, Queryable {
22
	
23
	/**
24
	 * The in-memory data.
25
	 * 
26
	 * @var array
27
	 */
28
	protected $data;
29
	
30
	/**
31
	 * Filters results in-memory.
32
	 * 
33
	 * @var Filterer
34
	 */
35
	protected $filterer;
36
	
37
	/**
38
	 * Sorts results in-memory.
39
	 * 
40
	 * @var Sorter
41
	 */
42
	protected $sorter;
43
	
44
	/**
45
	 * Create a new in-memory storage interface with the given data.
46
	 * 
47
	 * @param array $data [optional]
48
	 */
49
	public function __construct(array $data = array()) {
50
		$this->data = $data;
51
		$this->filterer = new Filterer;
52
		$this->sorter = new Sorter;
53
	}
54
	
55
	/**
56
	 * Limit the given data to the given length and offset.
57
	 * 
58
	 * @param array $data
59
	 * @param int   $limit  [optional]
60
	 * @param int   $offset [optional]
61
	 * @return array
62
	 */
63
	protected static function limit(array $data, $limit = 0, $offset = 0) {
64
		return array_slice($data, $offset, $limit ?: null);
65
	}
66
	
67
	/**
68
	 * Retrieve resource data using the given criteria.
69
	 * 
70
	 * Returns an array of associative arrays.
71
	 * 
72
	 * @param string       $resource
73
	 * @param array        $filter   [optional]
74
	 * @param array|string $order    [optional]
75
	 * @param int          $limit    [optional]
76
	 * @param int          $offset   [optional]
77
	 * @return array
78
	 */
79
	public function read($resource, array $filter = array(), $order = array(), $limit = 0, $offset = 0) {
80
		if (empty($this->data[$resource])) {
81
			return array();
82
		}
83
		
84
		$data = $this->filterer->filter($this->data[$resource], $filter);
85
		
86
		$data = $this->sorter->sort($data, $order);
87
		
88
		$data = static::limit($data, $limit, $offset);
89
		
90
		return $data;
91
	}
92
	
93
	/**
94
	 * Retrieve specific fields of a resource.
95
	 * 
96
	 * Returns an array of associative arrays.
97
	 * 
98
	 * @param string       $resource
99
	 * @param array|string $fields
100
	 * @param array        $filter   [optional]
101
	 * @param array|string $order    [optional]
102
	 * @param int          $limit    [optional]
103
	 * @param int          $offset   [optional]
104
	 * @return array
105
	 */
106
	public function listing($resource, $fields, array $filter = array(), $order = array(), $limit = 0, $offset = 0) {
107
		$data = $this->read($resource, $filter, $order, $limit, $offset);
108
		
109
		$fields = (array) $fields;
110
		
111
		$result = array();
112
		
113
		foreach ($data as $row) {
114
			$new = array();
115
			
116
			foreach ($row as $field => $value) {
117
				if (in_array($field, $fields)) {
118
					$new[$field] = $value;
119
				}
120
			}
121
			
122
			if (!empty($new)) {
123
				$result[] = $new;
124
			}
125
		}
126
		
127
		return $result;
128
	}
129
	
130
	/**
131
	 * Count the given resource with an optional filter.
132
	 * 
133
	 * @param string $resource
134
	 * @param array  $filter   [optional]
135
	 * @return int
136
	 */
137 View Code Duplication
	public function count($resource, array $filter = 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...
138
		if (empty($this->data[$resource])) {
139
			return 0;
140
		}
141
		
142
		return count($this->filterer->filter($this->data[$resource], $filter));
143
	}
144
	
145
	/**
146
	 * Create resource instances in the data store.
147
	 * 
148
	 * @param string $resource
149
	 * @param array  $data
150
	 * @return bool
151
	 */
152
	public function create($resource, $data) {
153
		if (!isset($this->data[$resource])) {
154
			$this->data[$resource] = array();
155
		}
156
		
157
		$this->data[$resource][] = $data;
158
		
159
		return true;
160
	}
161
	
162
	/**
163
	 * Update resource instances in the data store.
164
	 * 
165
	 * @param string $resource
166
	 * @param array  $data
167
	 * @param array  $filter   [optional]
168
	 * @param int    $limit    [optional]
169
	 * @return int|bool
170
	 */
171
	public function update($resource, $data, array $filter = array(), $limit = 0) {
172
		if (empty($this->data[$resource])) {
173
			return;
174
		}
175
		
176
		$affected = 0;
177
		
178
		$this->data[$resource] = $this->filterer->map(
179
			$this->data[$resource],
180
			$filter,
181
			function ($row) use ($data, &$affected) {
182
				foreach ($data as $key => $value) {
183
					$row[$key] = $value;
184
				}
185
				
186
				$affected++;
187
				
188
				return $row;
189
			},
190
			$limit
191
		);
192
		
193
		return $affected;
194
	}
195
	
196
	/**
197
	 * Delete resource instances from the data store.
198
	 * 
199
	 * @param string $resource
200
	 * @param array  $filter   [optional]
201
	 * @param int    $limit    [optional]
202
	 * @return int|bool
203
	 */
204 View Code Duplication
	public function delete($resource, array $filter = array(), $limit = 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...
205
		if (empty($this->data[$resource])) {
206
			return;
207
		}
208
		
209
		$this->data[$resource] = $this->filterer->reject($this->data[$resource], $filter);
210
	}
211
	
212
	/**
213
	 * Search for resource data with fields that match the given query and
214
	 * criteria.
215
	 * 
216
	 * @param string       $resource
217
	 * @param string       $query
218
	 * @param array|string $fields
219
	 * @param array        $filter   [optional]
220
	 * @param array|string $order    [optional]
221
	 * @param int          $limit    [optional]
222
	 * @param int          $offset   [optional]
223
	 * @return array
224
	 */
225
	public function search($resource, $query, $fields, array $filter = array(), $order = array(), $limit = null, $offset = 0) {
226
		if (empty($query) || empty($resource)) {
227
			return $this->read($resource, $filter, $order, $limit, $offset);
228
		}
229
		
230
		$fields = (array) $fields;
231
		$search = array('or' => array());
232
		
233
		foreach ($fields as $field) {
234
			$search['or']["$field like"] = "%$query%";
235
		}
236
		
237
		$filter = array_merge($filter, $search);
238
		
239
		return $this->read($resource, $filter, $order, $limit, $offset);
240
	}
241
	
242
	/**
243
	 * Retrieve the distinct values of the given resource's field.
244
	 * 
245
	 * Returns a flat array of values.
246
	 * 
247
	 * @param string $resource
248
	 * @param string $field
249
	 * @param array  $filter   [optional]
250
	 * @param array  $order    [optional]
251
	 * @param int    $limit    [optional]
252
	 * @param int    $offset   [optional]
253
	 * @return array
254
	 */
255
	public function distinct($resource, $field, array $filter = array(), $order = array(), $limit = 0, $offset = 0) {
256
		$list = array();
257
		
258
		$listing = $this->listing($resource, $field, $filter, $order, $limit, $offset);
259
		
260
		foreach ($listing as $item) {
261
			$list[] = $item[$field];
262
		}
263
		
264
		return array_unique($list);
265
	}
266
	
267
	/**
268
	 * Execute the given query.
269
	 * 
270
	 * @param Query $query
271
	 * @return Result
272
	 */
273
	public function execute(Query $query)
274
	{
275
		$data = array();
276
		$info = array();
277
		
278
		switch ($query->type) {
279
			case Query::CREATE:
280
				$this->create($query->resource, $query->data);
281
				break;
282
			case Query::READ:
283
				$data = $this->read(
284
					$query->resource,
285
					$query->filter,
286
					$query->order,
287
					$query->limit,
288
					$query->offset
289
				);
290
				break;
291
			case Query:UPDATE:
1 ignored issue
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
292
				$info['affected'] = $this->update(
293
					$query->resource,
294
					$query->data,
295
					$query->filter,
296
					$query->limit
297
				);
298
				break;
299
			case Query::DELETE:
300
				$this->delete(
301
					$query->resource,
302
					$query->filter,
303
					$query->limit
304
				);
305
				break;
306
		}
307
		
308
		return new Result($query, $data, $info);
309
	}
310
	
311
	/**
312
	 * Open a query on the given resource.
313
	 * 
314
	 * @param string $resource
315
	 * @param array  $fields   [optional]
316
	 * @return Query\Builder
317
	 */
318
	public function query($resource, $fields = array())
319
	{
320
		return new Query\Builder(new Query($resource, $fields), $this);
321
	}
322
	
323
	/**
324
	 * Retrieve the error that occured with the last operation.
325
	 * 
326
	 * Returns false if there was no error.
327
	 * 
328
	 * @return string|bool
329
	 */
330
	public function error() {
331
		return false;
332
	}
333
	
334
}
335