Passed
Push — master ( 650973...6b115b )
by Adrian
01:48
created

DbService::bindMore()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.2
c 0
b 0
f 0
cc 4
eloc 4
nc 3
nop 1
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * Author: Adrian Dumitru
5
 * Date: 6/1/2017 4:14 PM
6
 */
7
8
namespace Qpdb\QueryBuilder\DB;
9
10
11
class DbService
12
{
13
14
	const QUERY_TYPE_INSERT = 'INSERT';
15
	const QUERY_TYPE_DELETE = 'DELETE';
16
	const QUERY_TYPE_UPDATE = 'UPDATE';
17
	const QUERY_TYPE_SELECT = 'SELECT';
18
	const QUERY_TYPE_REPLACE = 'REPLACE';
19
	const QUERY_TYPE_SHOW = 'SHOW';
20
	const QUERY_TYPE_DESC = 'DESC';
21
	const QUERY_TYPE_OTHER = 'OTHER';
22
	const QUERY_TYPE_EXPLAIN = 'EXPLAIN';
23
24
	const ON_ERROR_THROW_EXCEPTION = 1;
25
	const ON_ERROR_RETURN_ERROR = 2;
26
27
	const QUERY_RESULT_TYPE_ARRAY = 'array';
28
	const QUERY_RESULT_TYPE_NUMBER = 'number';
29
	const QUERY_RESULT_TYPE_NULL = 'null';
30
31
	/**
32
	 * @var DbService
33
	 */
34
	private static $instance;
35
36
	/**
37
	 * @var \PDO
38
	 */
39
	private $pdo;
40
41
	/**
42
	 * @var \PDOStatement
43
	 */
44
	private $sQuery;
45
46
	/**
47
	 * @var array
48
	 */
49
	private $parameters = [];
50
51
	/**
52
	 * @var string;
53
	 */
54
	private $lastStatement;
55
56
	/**
57
	 * @var bool
58
	 */
59
	private $forceToMaster = false;
60
61
62
	/**
63
	 * @return $this
64
	 */
65
	public function withMasterOnly()
66
	{
67
		$this->forceToMaster = true;
68
69
		return $this;
70
	}
71
72
	/**
73
	 * @return $this
74
	 */
75
	public function withOutMasterOnly()
76
	{
77
		$this->forceToMaster = false;
78
79
		return $this;
80
	}
81
82
	/**
83
	 * @param string $query
84
	 * @param array $params
85
	 * @param int $fetchMode
86
	 * @return array|int|null
87
	 */
88
	public function query( $query, $params = null, $fetchMode = \PDO::FETCH_ASSOC )
89
	{
90
91
		$this->queryInit( $query, $params );
92
		$resultType = $this->getResultType( $this->lastStatement );
93
94
		switch ( $resultType ) {
95
			case self::QUERY_RESULT_TYPE_ARRAY:
96
				$result = $this->sQuery->fetchAll( $fetchMode );
97
				break;
98
			case self::QUERY_RESULT_TYPE_NUMBER:
99
				$result = $this->sQuery->rowCount();
100
				break;
101
			default:
102
				$result = null;
103
				break;
104
		}
105
106
		$this->sQuery->closeCursor();
107
108
		return $result;
109
	}
110
111
	/**
112
	 * @param $query
113
	 * @param array $params
114
	 * @return array|null
115
	 */
116
	public function column( $query, $params = null )
117
	{
118
		$this->queryInit( $query, $params );
119
120
		if ( $this->lastStatement === self::QUERY_TYPE_EXPLAIN )
121
			return $this->sQuery->fetchAll( \PDO::FETCH_ASSOC );
122
123
		$Columns = $this->sQuery->fetchAll( \PDO::FETCH_NUM );
124
125
		$column = null;
126
127
		foreach ( $Columns as $cells ) {
128
			$column[] = $cells[ 0 ];
129
		}
130
131
		return $column;
132
	}
133
134
	/**
135
	 * @param string $query
136
	 * @param array $params
137
	 * @param int $fetchmode
138
	 * @return array|mixed
139
	 */
140 View Code Duplication
	public function row( $query, $params = null, $fetchmode = \PDO::FETCH_ASSOC )
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...
141
	{
142
		$this->queryInit( $query, $params );
143
144
		if ( $this->lastStatement === self::QUERY_TYPE_EXPLAIN )
145
			return $this->sQuery->fetchAll( \PDO::FETCH_ASSOC );
146
147
		$result = $this->sQuery->fetch( $fetchmode );
148
		$this->sQuery->closeCursor(); // Frees up the connection to the server so that other SQL statements may be issued,
149
150
		return $result;
151
	}
152
153
	/**
154
	 * @param string $query
155
	 * @param array $params
156
	 * @return mixed|array
157
	 */
158 View Code Duplication
	public function single( $query, $params = 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...
159
	{
160
		$this->queryInit( $query, $params );
161
162
		if ( $this->lastStatement === self::QUERY_TYPE_EXPLAIN )
163
			return $this->sQuery->fetchAll( \PDO::FETCH_ASSOC );
164
165
		$result = $this->sQuery->fetchColumn();
166
		$this->sQuery->closeCursor(); // Frees up the connection to the server so that other SQL statements may be issued
167
168
		return $result;
169
	}
170
171
172
	/**
173
	 * @param string $statement
174
	 * @return string
175
	 */
176
	private function getResultType( $statement )
177
	{
178
		switch ( $statement ) {
179
180
			case self::QUERY_TYPE_SELECT:
181
			case self::QUERY_TYPE_SHOW:
182
			case self::QUERY_TYPE_DESC:
183
			case self::QUERY_TYPE_EXPLAIN:
184
				return self::QUERY_RESULT_TYPE_ARRAY;
185
186
			case self::QUERY_TYPE_INSERT:
187
			case self::QUERY_TYPE_UPDATE:
188
			case self::QUERY_TYPE_DELETE:
189
				return self::QUERY_RESULT_TYPE_NUMBER;
190
191
			default:
192
				return self::QUERY_RESULT_TYPE_NULL;
193
194
		}
195
	}
196
197
	/**
198
	 * Create necessary type connection
199
	 */
200
	private function createPdoConnection()
201
	{
202
		$this->pdo = $this->forceToMaster
203
			? DbConnect::getInstance()->getMasterConnection()
204
			: DbConnect::getInstance()->getConnection( $this->lastStatement );
205
	}
206
207
	/**
208
	 * @param string $query
209
	 * @param array $parameters
210
	 * @throws DbException
211
	 */
212
	private function queryInit( $query, $parameters = [] )
213
	{
214
		$this->lastStatement = self::getQueryStatement( $query );
215
		$this->createPdoConnection();
216
		$startQueryTime = microtime( true );
217
218
		try {
219
			/**
220
			 * Prepare query
221
			 */
222
			$this->sQuery = $this->pdo->prepare( $query );
223
			$this->prepareParams($parameters);
224
			$this->pdoBindValues();
225
			$this->sQuery->execute();
226
			if ( DbConfig::getInstance()->isEnableLogQueryDuration() ) {
227
				$duration = microtime( true ) - $startQueryTime;
228
				DbLog::getInstance()->writeQueryDuration( $query, $duration );
229
			}
230
		} catch ( \PDOException $e ) {
231
			if ( DbConfig::getInstance()->isEnableLogErrors() ) {
232
				DbLog::getInstance()->writeQueryErrors( $query, $e );
233
			}
234
			throw new DbException( 'Database query runtime error!', DbException::DB_QUERY_ERROR );
235
		}
236
237
		/**
238
		 * Reset the parameters
239
		 */
240
		$this->parameters = array();
241
	}
242
243
	private function prepareParams( array $parameters )
244
	{
245
		if ( self::isArrayAssoc( $parameters ) )
246
			$this->bindMore( $parameters );
247
		else
248
			foreach ( $parameters as $key => $val )
249
				$this->parameters[] = array( $key + 1, $val );
250
	}
251
252
	private function pdoBindValues()
253
	{
254
		foreach ( $this->parameters as $param => $value ) {
255
			if ( is_int( $value[ 1 ] ) ) {
256
				$type = \PDO::PARAM_INT;
257
			}
258
			elseif ( is_bool( $value[ 1 ] ) ) {
259
				$type = \PDO::PARAM_BOOL;
260
			}
261
			elseif ( is_null( $value[ 1 ] ) ) {
262
				$type = \PDO::PARAM_NULL;
263
			}
264
			else {
265
				$type = \PDO::PARAM_STR;
266
			}
267
			$this->sQuery->bindValue( $value[ 0 ], $value[ 1 ], $type );
268
		}
269
	}
270
271
	/**
272
	 * @param array $parray
273
	 */
274
	public function bindMore( $parray )
275
	{
276
		if ( !count( $this->parameters ) && is_array( $parray ) ) {
277
			$columns = array_keys( $parray );
278
			foreach ( $columns as $i => &$column ) {
279
				$this->bind( $column, $parray[ $column ] );
280
			}
281
		}
282
	}
283
284
	/**
285
	 * @param int|string $para
286
	 * @param mixed $value
287
	 */
288
	public function bind( $para, $value )
289
	{
290
		$this->parameters[ sizeof( $this->parameters ) ] = [ ":" . $para, $value ];
291
	}
292
293
294
	public function CloseConnection()
295
	{
296
		$this->pdo = null;
297
	}
298
299
300
	/**
301
	 * @param $queryString
302
	 * @return string
303
	 */
304
	public static function getQueryStatement( $queryString )
305
	{
306
		$queryString = trim( $queryString );
307
308
		if ( preg_match( '/^(select|insert|update|delete|replace|show|desc|explain)[\s]+/i', $queryString, $matches ) )
309
			return strtoupper( $matches[ 1 ] );
310
		else
311
			return self::QUERY_TYPE_OTHER;
312
	}
313
314
	/**
315
	 * @param array $arr
316
	 * @return bool
317
	 */
318
	public static function isArrayAssoc( array $arr )
319
	{
320
		if ( array() === $arr )
321
			return false;
322
323
		return array_keys( $arr ) !== range( 0, count( $arr ) - 1 );
324
	}
325
326
327
	/**
328
	 * @return DbService
329
	 */
330
	public static function getInstance()
331
	{
332
		if ( null === self::$instance ) {
333
			self::$instance = new self();
334
		}
335
336
		return self::$instance;
337
	}
338
339
}