Passed
Push — 1.0.0-dev ( 29a38b...3f14f3 )
by nguereza
02:39
created

Database::updateProperties()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 4
nop 0
dl 0
loc 11
rs 10
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 statments query INSERT, UPDATE
71
         * @var array
72
         */
73
        private $data = array();
74
	
75
        /**
76
         * The cache default time to live in second. 0 means no need to use the cache feature
77
         * @var int
78
         */
79
        private $cacheTtl = 0;
80
81
        /**
82
         * The cache current time to live. 0 means no need to use the cache feature
83
         * @var int
84
         */
85
        private $temporaryCacheTtl = 0;
86
87
        /**
88
         * The DatabaseQueryBuilder instance
89
         * @var object
90
         */
91
        protected $queryBuilder = null;
92
    
93
        /**
94
         * The DatabaseQueryRunner instance
95
         * @var object
96
         */
97
        protected $queryRunner = null;
98
99
        /**
100
         * The DatabaseCache instance
101
         * @var object
102
         */
103
        protected $cacheInstance = null;
104
105
        /**
106
         * Construct new instance
107
         * 
108
         * @param object $connection the DatabaseConnection instance
109
         */
110
        public function __construct(DatabaseConnection $connection = null) {
111
            parent::__construct();
112
    		
113
            //Set DatabaseQueryBuilder instance to use
114
            $this->setDependencyInstanceFromParamOrCreate('queryBuilder', null, 'DatabaseQueryBuilder', 'classes/database');
115
           
116
            //Set DatabaseQueryRunner instance to use
117
            $this->setDependencyInstanceFromParamOrCreate('queryRunner', null, 'DatabaseQueryRunner', 'classes/database');
118
119
            //Set DatabaseCache instance to use
120
            $this->setDependencyInstanceFromParamOrCreate('cacheInstance', null, 'DatabaseCache', 'classes/database');
121
122
            if ($connection !== null) {
123
                $this->connection = $connection;
124
            } else {
125
                $this->setConnectionUsingConfigFile();
126
            }
127
            //Update some properties
128
            $this->updateProperties();
129
        }
130
131
        /**
132
         * Return the number of rows returned by the current query
133
         * @return int
134
         */
135
        public function numRows() {
136
            return $this->numRows;
137
        }
138
139
        /**
140
         * Return the last insert id value
141
         * @return mixed
142
         */
143
        public function insertId() {
144
            return $this->insertId;
145
        }
146
147
        /**
148
         * Get the result of one record rows returned by the current query
149
         * @param  boolean $returnSQLQueryOrResultType if is boolean and true will return the SQL query string.
150
         * If is string will determine the result type "array" or "object"
151
         * @return mixed       the query SQL string or the record result
152
         */
153
        public function get($returnSQLQueryOrResultType = false) {
154
            $this->queryBuilder->limit(1);
155
            $query = $this->getAll($returnSql = true);
156
            if ($returnSQLQueryOrResultType === true) {
157
                return $query;
158
            } else {
159
                return $this->query($query, false, $returnSQLQueryOrResultType == 'array');
160
            }
161
        }
162
163
        /**
164
         * Get the result of record rows list returned by the current query
165
         * @param  boolean|string $returnSQLQueryOrResultType if is boolean and true will return the SQL query string.
166
         * If is string will determine the result type "array" or "object"
167
         * @return mixed       the query SQL string or the record result
168
         */
169
        public function getAll($returnSQLQueryOrResultType = false) {
170
            $query = $this->queryBuilder->getQuery();
171
            if ($returnSQLQueryOrResultType === true) {
172
                return $query;
173
            }
174
            return $this->query($query, true, $returnSQLQueryOrResultType == 'array');
175
        }
176
177
        /**
178
         * Insert new record in the database
179
         * @param  array   $data   the record data if is empty will use the $this->data array.
180
         * @param  boolean $escape  whether to escape or not the values
181
         * @return mixed          the insert id of the new record or null
182
         */
183
        public function insert($data = array(), $escape = true) {
184
            if (empty($data) && !empty($this->data)) {
185
                //as when using $this->setData() may be the data already escaped
186
                $escape = false;
187
                $data = $this->data;
188
            }
189
            $query = $this->queryBuilder->insert($data, $escape)->getQuery();
190
            $result = $this->query($query);
191
            if ($result) {
192
                $this->insertId = $this->connection->getPdo()->lastInsertId();
193
                //if the table doesn't have the auto increment field or sequence, the value of 0 will be returned 
194
                $id = $this->insertId;
195
                if (!$id) {
196
                    $id = true;
197
                }
198
                return $id;
199
            }
200
            return false;
201
        }
202
203
        /**
204
         * Update record in the database
205
         * @param  array   $data   the record data if is empty will use the $this->data array.
206
         * @param  boolean $escape  whether to escape or not the values
207
         * @return mixed          the update status
208
         */
209
        public function update($data = array(), $escape = true) {
210
            if (empty($data) && !empty($this->data)) {
211
                //as when using $this->setData() may be the data already escaped
212
                $escape = false;
213
                $data = $this->data;
214
            }
215
            $query = $this->queryBuilder->update($data, $escape)->getQuery();
216
            return $this->query($query);
217
        }
218
219
        /**
220
         * Delete the record in database
221
         * @return mixed the delete status
222
         */
223
        public function delete() {
224
            $query = $this->queryBuilder->delete()->getQuery();
225
            return $this->query($query);
226
        }
227
228
        /**
229
         * Set database cache time to live
230
         * @param integer $ttl the cache time to live in second
231
         * @return object        the current instance
232
         */
233
        public function setCache($ttl = 0) {
234
            $this->cacheTtl = $ttl;
235
            $this->temporaryCacheTtl = $ttl;
236
            return $this;
237
        }
238
	
239
        /**
240
         * Enabled cache temporary for the current query not globally   
241
         * @param  integer $ttl the cache time to live in second
242
         * @return object        the current instance
243
         */
244
        public function cached($ttl = 0) {
245
            $this->temporaryCacheTtl = $ttl;
246
            return $this;
247
        }
248
249
        /**
250
         * Return the number query executed count for the current request
251
         * @return int
252
         */
253
        public function queryCount() {
254
            return $this->queryCount;
255
        }
256
257
        /**
258
         * Return the current query SQL string
259
         * @return string
260
         */
261
        public function getQuery() {
262
            return $this->query;
263
        }
264
265
        /**
266
         * Return the DatabaseConnection instance
267
         * @return object DatabaseConnection
268
         */
269
        public function getConnection() {
270
            return $this->connection;
271
        }
272
273
        /**
274
         * Set the DatabaseConnection instance
275
         * @param object DatabaseConnection $connection the DatabaseConnection object
276
         *
277
         * @return object the current instance
278
         */
279
        public function setConnection(DatabaseConnection $connection) {
280
            $this->connection = $connection;
281
            return $this;
282
        }
283
284
        /**
285
         * Return the DatabaseQueryBuilder instance
286
         * @return object DatabaseQueryBuilder
287
         */
288
        public function getQueryBuilder() {
289
            return $this->queryBuilder;
290
        }
291
292
        /**
293
         * Set the DatabaseQueryBuilder instance
294
         * @param object DatabaseQueryBuilder $queryBuilder the DatabaseQueryBuilder object
295
         */
296
        public function setQueryBuilder(DatabaseQueryBuilder $queryBuilder) {
297
            $this->queryBuilder = $queryBuilder;
298
            return $this;
299
        }
300
301
        /**
302
         * Return the DatabaseCache instance
303
         * @return object DatabaseCache
304
         */
305
        public function getCacheInstance() {
306
            return $this->cacheInstance;
307
        }
308
309
        /**
310
         * Set the DatabaseCache instance
311
         * @param object DatabaseCache $cacheInstance the DatabaseCache object
312
         */
313
        public function setCacheInstance(DatabaseCache $cacheInstance) {
314
            $this->cacheInstance = $cacheInstance;
315
            return $this;
316
        }
317
    
318
        /**
319
         * Return the DatabaseQueryRunner instance
320
         * @return object DatabaseQueryRunner
321
         */
322
        public function getQueryRunner() {
323
            return $this->queryRunner;
324
        }
325
326
        /**
327
         * Set the DatabaseQueryRunner instance
328
         * @param object DatabaseQueryRunner $queryRunner the DatabaseQueryRunner object
329
         */
330
        public function setQueryRunner(DatabaseQueryRunner $queryRunner) {
331
            $this->queryRunner = $queryRunner;
332
            return $this;
333
        }
334
335
        /**
336
         * Return the data to be used for insert, update, etc.
337
         * @return array
338
         */
339
        public function getData() {
340
            return $this->data;
341
        }
342
343
        /**
344
         * Set the data to be used for insert, update, etc.
345
         * @param string|array $key the data key identified
346
         * @param mixed $value the data value
347
         * @param boolean $escape whether to escape or not the $value
348
         * @return object        the current Database instance
349
         */
350
        public function setData($key, $value = null, $escape = true) {
351
            if (is_array($key)) {
352
                foreach ($key as $k => $v) {
353
                    $this->setData($k, $v, $escape);
354
                }	
355
            } else {
356
            $this->data[$key] = $this->connection->escape($value, $escape);
357
            }
358
            return $this;
359
        }
360
361
        /**
362
         * Execute an SQL query
363
         * @param  string  $query the query SQL string
364
         * @param  boolean $returnAsList  indicate whether to return all record or just one row 
365
         * @param  boolean $returnAsArray return the result as array or not
366
         * @return mixed         the query result
367
         */
368
        public function query($query, $returnAsList = true, $returnAsArray = false) {
369
            $this->reset();
370
            $this->query = preg_replace('/\s\s+|\t\t+/', ' ', trim($query));
371
            $this->logger->info('Execute SQL query [' . $this->query . ']');
372
373
            $cacheExpire = $this->temporaryCacheTtl;
374
375
            //return to the initial cache time
376
            $this->temporaryCacheTtl = $this->cacheTtl;
377
378
            //the database cache content
379
            $cacheContent = $this->cacheInstance->setQuery($query)
380
                                                ->setReturnType($returnAsList)
381
                                                ->setReturnAsArray($returnAsArray)
382
                                                ->setCacheTtl($cacheExpire)
383
                                                ->getCacheContent();
384
            if (!$cacheContent) {
385
                $this->logger->info('No cache data found for this query or is not a SELECT query, get result from real database');
386
                //count the number of query execution to server
387
                $this->queryCount++;
388
                
389
                $queryResult = $this->queryRunner->setQuery($query)
390
                                                 ->setReturnType($returnAsList)
391
                                                 ->setReturnAsArray($returnAsArray)
392
                                                 ->execute();
393
394
                if (is_object($queryResult)) {
395
                    $this->result  = $queryResult->getResult();
396
                    $this->numRows = $queryResult->getNumRows();
397
                    //save the result into cache
398
                    $this->cacheInstance->saveCacheContent($this->result);
399
                }
400
            } else {
401
                $this->logger->info('The result for query [' . $this->query . '] already cached use it');
402
                $this->result = $cacheContent;
403
                $this->numRows = count($this->result);
404
            }
405
            return $this->result;
406
        }
407
408
         /**
409
         * Set the connection instance using database configuration file
410
         *
411
         * @return object|void
412
         */
413
        protected function setConnectionUsingConfigFile(){
414
            $dbConfigFromFile = $this->getDatabaseConfigFromFile();
415
            $connection = &class_loader('DatabaseConnection', 'classes/database');
416
            $connection->setConfig($dbConfigFromFile);
417
            $connection->connect();
418
            $this->connection = $connection;
419
            return $this;
420
        }
421
422
423
        /**
424
         * Get the database configuration using the configuration file
425
         
426
         * @return array the database configuration from file
427
         */
428
        protected function getDatabaseConfigFromFile() {
429
            $db = array();
430
            if (file_exists(CONFIG_PATH . 'database.php')) {
431
                //here don't use require_once because somewhere user can create database instance directly
432
                require CONFIG_PATH . 'database.php';
433
            }
434
            return $db;
435
        }
436
437
        /**
438
         * Update the dependency for some properties
439
         * @return object the current instance
440
         */
441
        protected function updateProperties() {
442
            //update queryBuilder with some properties needed
443
            if (is_object($this->queryBuilder)) {
444
                $this->queryBuilder->setConnection($this->connection);
445
            }
446
447
            //update queryRunner with some properties needed
448
            if (is_object($this->queryRunner)) {
449
                $this->queryRunner->setConnection($this->connection);
450
            }
451
            return $this;
452
        }
453
	
454
        /**
455
         * Reset the database class attributs to the initail values before each query.
456
         */
457
        private function reset() {
458
            //query builder reset
459
            $this->queryBuilder->reset();
460
            $this->numRows  = 0;
461
            $this->insertId = null;
462
            $this->query    = null;
463
            $this->result   = array();
464
            $this->data     = array();
465
        }
466
    }
467