Database   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 408
Duplicated Lines 0 %

Importance

Changes 5
Bugs 1 Features 2
Metric Value
eloc 112
c 5
b 1
f 2
dl 0
loc 408
rs 9.1199
wmc 41

27 Methods

Rating   Name   Duplication   Size   Complexity  
A getCache() 0 2 1
A getQuery() 0 2 1
A update() 0 8 3
A setConnection() 0 3 1
A setCache() 0 3 1
A cached() 0 3 1
A delete() 0 3 1
A getConnection() 0 2 1
A getCacheTimeToLive() 0 2 1
A insertId() 0 2 1
A get() 0 7 2
A getQueryBuilder() 0 2 1
A getTempCacheTimeToLive() 0 2 1
A getData() 0 2 1
A setQueryRunner() 0 3 1
A insert() 0 13 4
A getAll() 0 6 2
A queryCount() 0 2 1
A setCacheTimeToLive() 0 4 1
A setQueryBuilder() 0 3 1
A numRows() 0 2 1
A setData() 0 9 3
A getQueryRunner() 0 2 1
A __construct() 0 4 2
A query() 0 43 4
A setLastInsertId() 0 9 2
A reset() 0 9 1

How to fix   Complexity   

Complex Class

Complex classes like Database often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Database, and based on these observations, apply Extract Interface, too.

1
<?php
2
    defined('ROOT_PATH') || exit('Access denied');
3
    /**
4
     * TNH Framework
5
     *
6
     * A simple PHP framework using HMVC architecture
7
     *
8
     * This content is released under the MIT License (MIT)
9
     *
10
     * Copyright (c) 2017 TNH Framework
11
     *
12
     * Permission is hereby granted, free of charge, to any person obtaining a copy
13
     * of this software and associated documentation files (the "Software"), to deal
14
     * in the Software without restriction, including without limitation the rights
15
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
     * copies of the Software, and to permit persons to whom the Software is
17
     * furnished to do so, subject to the following conditions:
18
     *
19
     * The above copyright notice and this permission notice shall be included in all
20
     * copies or substantial portions of the Software.
21
     *
22
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
     * SOFTWARE.
29
     */
30
    
31
    class Database extends BaseClass {
32
	
33
        /**
34
         * The DatabaseConnection instance
35
         * @var object
36
         */
37
        private $connection = null;
38
    
39
        /**
40
         * The number of rows returned by the last query
41
         * @var int
42
         */
43
        private $numRows = 0;
44
	
45
        /**
46
         * The last insert id for the primary key column that have auto increment or sequence
47
         * @var mixed
48
         */
49
        private $insertId = null;
50
	
51
        /**
52
         * The full SQL query statment after build for each command
53
         * @var string
54
         */
55
        private $query = null;
56
	
57
        /**
58
         * The result returned for the last query
59
         * @var mixed
60
         */
61
        private $result = array();
62
	
63
        /**
64
         * The number of executed query for the current request
65
         * @var int
66
         */
67
        private $queryCount = 0;
68
	
69
        /**
70
         * The default data to be used in the INSERT, UPDATE statments 
71
         * @var array
72
         */
73
        private $data = array();
74
	
75
        /**
76
         * The cache default time to live in second. 0 means no need 
77
         * to use the cache feature
78
         * @var int
79
         */
80
        private $cacheTtl = 0;
81
82
        /**
83
         * The cache current time to live. 0 means no need 
84
         * to use the cache feature
85
         * @var int
86
         */
87
        private $temporaryCacheTtl = 0;
88
89
        /**
90
         * The DatabaseQueryBuilder instance
91
         * @var object
92
         */
93
        protected $queryBuilder = null;
94
    
95
        /**
96
         * The DatabaseQueryRunner instance
97
         * @var object
98
         */
99
        protected $queryRunner = null;
100
101
        /**
102
         * The DatabaseCache instance
103
         * @var object
104
         */
105
        protected $cache = null;
106
107
        /**
108
         * Construct new instance
109
         * 
110
         * @param object $connection the DatabaseConnection instance
111
         */
112
        public function __construct(DatabaseConnection $connection = null) {
113
            parent::__construct();
114
    		if ($connection !== null) {
115
                $this->connection = $connection;
116
            } 
117
        }
118
119
        /**
120
         * Get the result of one record rows returned by the current query
121
         * @param  boolean|string $sqlOrResult if is boolean and true will return the SQL query string.
122
         * If is string will determine the result type "array" or "object"
123
         * @return mixed       the query SQL string or the record result
124
         */
125
        public function get($sqlOrResult = false) {
126
            $this->queryBuilder->limit(1);
127
            $query = $this->getAll(true);
128
            if ($sqlOrResult === true) {
129
                return $query;
130
            } 
131
            return $this->query($query, false, $sqlOrResult == 'array');
132
        }
133
134
        /**
135
         * Get the result of record rows list returned by the current query
136
         * @param  boolean|string $sqlOrResult if is boolean and true will return the SQL query string.
137
         * If is string will determine the result type "array" or "object"
138
         * @return mixed       the query SQL string or the record result
139
         */
140
        public function getAll($sqlOrResult = false) {
141
            $query = $this->queryBuilder->getQuery();
142
            if ($sqlOrResult === true) {
143
                return $query;
144
            }
145
            return $this->query($query, true, $sqlOrResult == 'array');
146
        }
147
148
        /**
149
         * Insert new record in the database
150
         * @param  array   $data   the record data if is empty will use the $this->data array.
151
         * @param  boolean $escape  whether to escape or not the values
152
         * @return mixed          the insert id of the new record or null
153
         */
154
        public function insert($data = array(), $escape = true) {
155
            if (empty($data) && !empty($this->data)) {
156
                //as when using $this->setData() may be the data already escaped
157
                $escape = false;
158
                $data = $this->data;
159
            }
160
            $query = $this->queryBuilder->insert($data, $escape)->getQuery();
161
            $result = $this->query($query);
162
            if ($result) {
163
                $this->setLastInsertId();
164
                return $this->insertId;
165
            }
166
            return false;
167
        }
168
169
        /**
170
         * Update record in the database
171
         * @param  array   $data   the record data if is empty will use the $this->data array.
172
         * @param  boolean $escape  whether to escape or not the values
173
         * @return mixed          the update status
174
         */
175
        public function update($data = array(), $escape = true) {
176
            if (empty($data) && !empty($this->data)) {
177
                //as when using $this->setData() may be the data already escaped
178
                $escape = false;
179
                $data = $this->data;
180
            }
181
            $query = $this->queryBuilder->update($data, $escape)->getQuery();
182
            return $this->query($query);
183
        }
184
185
        /**
186
         * Delete the record in database
187
         * @return mixed the delete status
188
         */
189
        public function delete() {
190
            $query = $this->queryBuilder->delete()->getQuery();
191
            return $this->query($query);
192
        }
193
194
        /**
195
         * Set database cache time to live
196
         * @param integer $ttl the cache time to live in second
197
         * @return object        the current instance
198
         */
199
        public function setCacheTimeToLive($ttl = 0) {
200
            $this->cacheTtl = $ttl;
201
            $this->temporaryCacheTtl = $ttl;
202
            return $this;
203
        }
204
205
        /**
206
         * Get database cache time to live
207
         * @return integer        
208
         */
209
        public function getCacheTimeToLive() {
210
            return $this->cacheTtl;
211
        }
212
213
        /**
214
         * Get database current cache time to live
215
         * @return integer        
216
         */
217
        public function getTempCacheTimeToLive() {
218
            return $this->temporaryCacheTtl;
219
        }
220
	
221
        /**
222
         * Enabled cache temporary for the current query not globally   
223
         * @param  integer $ttl the cache time to live in second
224
         * @return object        the current instance
225
         */
226
        public function cached($ttl = 0) {
227
            $this->temporaryCacheTtl = $ttl;
228
            return $this;
229
        }
230
231
        /**
232
         * Return the number query executed count for the current request
233
         * @return int
234
         */
235
        public function queryCount() {
236
            return $this->queryCount;
237
        }
238
239
        /**
240
         * Return the current query SQL string
241
         * @return string
242
         */
243
        public function getQuery() {
244
            return $this->query;
245
        }
246
247
        /**
248
         * Return the DatabaseConnection instance
249
         * @return object DatabaseConnection
250
         */
251
        public function getConnection() {
252
            return $this->connection;
253
        }
254
255
        /**
256
         * Set the DatabaseConnection instance
257
         * @param object DatabaseConnection $connection the DatabaseConnection object
258
         *
259
         * @return object the current instance
260
         */
261
        public function setConnection(DatabaseConnection $connection = null) {
262
            $this->connection = $connection;
263
            return $this;
264
        }
265
266
        /**
267
         * Return the DatabaseQueryBuilder instance
268
         * @return object DatabaseQueryBuilder
269
         */
270
        public function getQueryBuilder() {
271
            return $this->queryBuilder;
272
        }
273
274
        /**
275
         * Set the DatabaseQueryBuilder instance
276
         * @param object DatabaseQueryBuilder $queryBuilder the DatabaseQueryBuilder object
277
         */
278
        public function setQueryBuilder(DatabaseQueryBuilder $queryBuilder = null) {
279
            $this->queryBuilder = $queryBuilder;
280
            return $this;
281
        }
282
283
        /**
284
         * Return the DatabaseCache instance
285
         * @return object DatabaseCache
286
         */
287
        public function getCache() {
288
            return $this->cache;
289
        }
290
291
        /**
292
         * Set the DatabaseCache instance
293
         * @param object DatabaseCache $cache the DatabaseCache object
294
         */
295
        public function setCache(DatabaseCache $cache = null) {
296
            $this->cache = $cache;
297
            return $this;
298
        }
299
    
300
        /**
301
         * Return the DatabaseQueryRunner instance
302
         * @return object DatabaseQueryRunner
303
         */
304
        public function getQueryRunner() {
305
            return $this->queryRunner;
306
        }
307
308
        /**
309
         * Set the DatabaseQueryRunner instance
310
         * @param object DatabaseQueryRunner $queryRunner the DatabaseQueryRunner object
311
         */
312
        public function setQueryRunner(DatabaseQueryRunner $queryRunner = null) {
313
            $this->queryRunner = $queryRunner;
314
            return $this;
315
        }
316
317
        /**
318
         * Return the data to be used for insert, update, etc.
319
         * @return array
320
         */
321
        public function getData() {
322
            return $this->data;
323
        }
324
325
        /**
326
         * Set the data to be used for insert, update, etc.
327
         * @param string|array $key the data key identified
328
         * @param mixed $value the data value
329
         * @param boolean $escape whether to escape or not the $value
330
         * @return object        the current Database instance
331
         */
332
        public function setData($key, $value = null, $escape = true) {
333
            if (is_array($key)) {
334
                foreach ($key as $k => $v) {
335
                    $this->setData($k, $v, $escape);
336
                }	
337
            } else {
338
                $this->data[$key] = $this->connection->escape($value, $escape);
339
            }
340
            return $this;
341
        }
342
343
        /**
344
         * Return the number of rows returned by the current query
345
         * @return int
346
         */
347
        public function numRows() {
348
            return $this->numRows;
349
        }
350
351
        /**
352
         * Return the last insert id value
353
         * @return mixed
354
         */
355
        public function insertId() {
356
            return $this->insertId;
357
        }
358
359
        /**
360
         * Execute an SQL query
361
         * @param  string  $query the query SQL string
362
         * @param  boolean $returnAsList  indicate whether to return all record or just one row 
363
         * @param  boolean $returnAsArray return the result as array or not
364
         * @return mixed         the query result
365
         */
366
        public function query($query, $returnAsList = true, $returnAsArray = false) {
367
            $this->reset();
368
            $this->query = preg_replace('/\s\s+|\t\t+/', ' ', trim($query));
369
            $this->logger->info('Execute SQL query [' . $this->query . ']');
370
371
            $cacheExpire = $this->temporaryCacheTtl;
372
373
            //return to the initial cache time
374
            $this->temporaryCacheTtl = $this->cacheTtl;
375
376
            //the database cache content
377
            $cacheContent = $this->cache->setQuery($query)
378
                                        ->setReturnType($returnAsList)
379
                                        ->setReturnAsArray($returnAsArray)
380
                                        ->setCacheTtl($cacheExpire)
381
                                        ->getCacheContent();
382
            if (!$cacheContent) {
383
                $this->logger->info('No cache data found for this query or is not a SELECT query, get result from real database');
384
                //count the number of query execution to server
385
                $this->queryCount++;
386
                
387
                $queryResult = $this->queryRunner->setQuery($query)
388
                                                 ->setReturnType($returnAsList)
389
                                                 ->setReturnAsArray($returnAsArray)
390
                                                 ->execute();
391
392
                if (is_object($queryResult)) {
393
                    $this->result  = $queryResult->getResult();
394
                    $this->numRows = $queryResult->getNumRows();
395
                    //save the result into cache
396
                    $this->cache->setCacheContent($this->result);
397
                }
398
            } else {
399
                $this->logger->info('The result for query [' . $this->query . '] already cached use it');
400
                $this->result = $cacheContent;
401
                if ($returnAsList) {
402
                    $this->numRows = count($this->result);
403
                } else {
404
                    //if only one row will be returned
405
                    $this->numRows = 1;
406
                }
407
            }
408
            return $this->result;
409
        }
410
411
        /**
412
         * Set the last insert id value
413
         *
414
         * @return object the current instance
415
         */
416
        protected function setLastInsertId() {
417
            $id = $this->connection->getPdo()->lastInsertId();
418
            //if the table doesn't have the auto increment field or sequence,
419
            // the value of 0 will be returned 
420
            if (!$id) {
421
                $id = true;
422
            }
423
            $this->insertId = $id;
424
            return $this;
425
        }
426
427
        /**
428
         * Reset the database class attributs to the initial values before each query.
429
         */
430
        protected function reset() {
431
            //query builder reset
432
            $this->queryBuilder->reset();
433
            $this->numRows    = 0;
434
            $this->insertId   = null;
435
            $this->query      = null;
436
            $this->result     = array();
437
            $this->data       = array();
438
            $this->queryCount = 0;
439
        }
440
    }
441