Completed
Push — master ( 319aa4...523298 )
by smiley
04:24
created

DBDriverAbstract::rawCached()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 12
Ratio 100 %

Importance

Changes 0
Metric Value
dl 12
loc 12
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 3
1
<?php
2
/**
3
 * @filesource   DBDriverAbstract.php
4
 * @created      04.11.2015
5
 * @package      chillerlan\Database\Drivers
6
 * @author       Smiley <[email protected]>
7
 * @copyright    2015 Smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\Database\Drivers;
12
13
use chillerlan\Database\DBException;
14
use chillerlan\Database\DBOptions;
15
use chillerlan\Database\DBResult;
16
use Psr\SimpleCache\CacheInterface;
17
18
/**
19
 * Class DBDriverAbstract
20
 *
21
 * Implements common methods of DBDriverInterface - this class shall only be extended, never instanced
22
 *
23
 * @see http://blog.mclaughlinsoftware.com/2010/02/21/php-binding-a-wildcard/ LIKE %...% -> LIKE CONCAT('%',?,'%')
24
 */
25
abstract class DBDriverAbstract implements DBDriverInterface{
26
27
	/**
28
	 * Holds the database resource object
29
	 *
30
	 * @var resource
31
	 */
32
	protected $db;
33
34
	/**
35
	 * Holds the settings
36
	 *
37
	 * @var \chillerlan\Database\DBOptions
38
	 */
39
	protected $options;
40
41
	/**
42
	 * @var \Psr\SimpleCache\CacheInterface
43
	 */
44
	protected $cacheDriver;
45
46
	abstract protected function __escape($data);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
47
48
	abstract protected function __raw(string $sql, string $index = null, bool $assoc = true);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
49
50
	abstract protected function __prepared(string $sql, array $values = [], string $index = null, bool $assoc = true);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
51
52
	abstract protected function __multi(string $sql, array $values);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
53
54
	abstract protected function __multi_callback(string $sql, array $data, $callback);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
55
56
	/**
57
	 * Constructor.
58
	 *
59
	 * @param \chillerlan\Database\DBOptions       $options
60
	 * @param \Psr\SimpleCache\CacheInterface|null $cacheDriver
61
	 */
62
	public function __construct(DBOptions $options, CacheInterface $cacheDriver = null){
63
		$this->options     = $options;
64
		$this->cacheDriver = $cacheDriver;
65
	}
66
67
	/**
68
	 * Returns the plain connection object
69
	 *
70
	 * @return resource the database resource object
71
	 */
72
	public function getDBResource(){
73
		return $this->db;
74
	}
75
76
	/**
77
	 * @param array|string $data
78
	 * @param bool         $specialchars
79
	 *
80
	 * @return array|string
81
	 */
82 View Code Duplication
	public function escape($data, bool $specialchars = 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...
83
84
		if(is_array($data)){
85
86
			foreach($data as $key => $value){
87
				$data[$key] = $this->escape($value, $specialchars);
88
			}
89
90
		}
91
		else if($data instanceof \stdClass){
92
93
			foreach($data as $key => $value){
94
				$data->{$key} = $this->escape($value, $specialchars);
95
			}
96
97
		}
98
		else{
99
100
			if($specialchars){
101
				$data = htmlspecialchars($data, ENT_HTML5, 'UTF-8', false);
102
			}
103
104
			$data = $this->__escape($data);
105
		}
106
107
		return $data;
108
109
	}
110
111
	/**
112
	 * @param             $callable
113
	 * @param array       $args
114
	 * @param string|null $index
115
	 * @param bool        $assoc
116
	 *
117
	 * @return bool|\chillerlan\Database\DBResult
118
	 */
119
	protected function getResult($callable, array $args, string $index = null, bool $assoc){
120
		$out = new DBResult;
121
		$i   = 0;
122
123
		while($row = call_user_func_array($callable, $args)){
124
			$key = $i;
125
126
			if($assoc && !empty($index)){
127
				$key = $row[$index];
128
			}
129
130
			$out[$key] = $row;
131
			$i++;
132
		}
133
134
		return $i === 0 ? true : $out;
135
	}
136
137
	/**
138
	 * @param string      $sql
139
	 * @param string|null $index
140
	 * @param bool        $assoc
141
	 *
142
	 * @return mixed
143
	 * @throws \chillerlan\Database\DBException
144
	 */
145
	public function raw(string $sql, string $index = null, bool $assoc = true){
146
147
		try{
148
			return $this->__raw($sql, $index, $assoc);
149
		}
150
		catch(\Exception $e){
151
			throw new DBException('sql error: [raw]'.$e->getMessage());
152
		}
153
154
	}
155
156
	/**
157
	 * @param string      $sql
158
	 * @param array       $values
159
	 * @param string|null $index
160
	 * @param bool        $assoc
161
	 *
162
	 * @return mixed
163
	 * @throws \chillerlan\Database\DBException
164
	 */
165
	public function prepared(string $sql, array $values = [], string $index = null, bool $assoc = true){
166
167
		try{
168
			return $this->__prepared($sql, $values, $index, $assoc);
169
		}
170
		catch(\Exception $e){
171
			throw new DBException('sql error: [prepared] '.$e->getMessage());
172
		}
173
174
	}
175
176
	/**
177
	 * @param string $sql
178
	 * @param array  $values
179
	 *
180
	 * @return mixed
181
	 * @throws \chillerlan\Database\DBException
182
	 */
183
	public function multi(string $sql, array $values){
184
185
		if(!is_array($values) || count($values) < 1 || !is_array($values[0]) || count($values[0]) < 1){
186
			throw new DBException('invalid data');
187
		}
188
189
		try{
190
			return $this->__multi($sql, $values);
191
		}
192
		catch(\Exception $e){
193
			throw new DBException('sql error: [multi] '.$e->getMessage());
194
		}
195
196
	}
197
198
	/**
199
	 * @param string         $sql
200
	 * @param array          $data
201
	 * @param array|callable $callback
202
	 *
203
	 * @return mixed
204
	 * @throws \chillerlan\Database\DBException
205
	 */
206
	public function multi_callback(string $sql, array $data, $callback){
207
208
		if(count($data) < 1){
209
			throw new DBException('invalid data');
210
		}
211
212
		if(!is_callable($callback)){
213
			throw new DBException('invalid callback');
214
		}
215
216
		try{
217
			return $this->__multi_callback($sql, $data, $callback);
218
		}
219
		catch(\Exception $e){
220
			throw new DBException('sql error: [multi_callback] '.$e->getMessage());
221
		}
222
223
	}
224
225
	protected function cacheGet(string $sql, array $values = []){
226
227
		if($this->cacheDriver){
228
			return $this->cacheDriver->get($this->cacheKey($sql, $values));
229
		}
230
231
		return false;
232
	}
233
234
	protected function cacheSet(string $sql, array $values = [], $response):bool{
235
236
		if($this->cacheDriver){
237
			// @todo: TTL
238
			return $this->cacheDriver->set($this->cacheKey($sql, $values), $response);
239
		}
240
241
		return false;
242
	}
243
244
	protected function cacheKey(string $sql, array $values):string{
245
		return hash('sha256', $sql.serialize($values));
246
	}
247
248 View Code Duplication
	public function rawCached(string $sql, string $index = null, bool $assoc = true){
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...
249
250
		$result = $this->cacheGet($sql.$index);
251
252
		if(!$result){
253
			$result = $this->raw($sql, $index, $assoc);
254
255
			$this->cacheSet($sql.$index, [], $result);
256
		}
257
258
		return $result;
259
	}
260
261 View Code Duplication
	public function preparedCached(string $sql, array $values = [], string $index = null, bool $assoc = true){
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...
262
		$result = $this->cacheGet($sql.$index, $values);
263
264
		if(!$result){
265
			$result = $this->prepared($sql, $values, $index, $assoc);
266
267
			$this->cacheSet($sql.$index, $values, $result);
268
		}
269
270
		return $result;
271
	}
272
}
273