GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (423)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

system/database/DB_driver.php (11 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 53 and the first side effect is on line 38.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2015, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	http://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
40
/**
41
 * Database Driver Class
42
 *
43
 * This is the platform-independent base DB implementation class.
44
 * This class will not be called directly. Rather, the adapter
45
 * class for the specific database will extend and instantiate it.
46
 *
47
 * @package		CodeIgniter
48
 * @subpackage	Drivers
49
 * @category	Database
50
 * @author		EllisLab Dev Team
51
 * @link		http://codeigniter.com/user_guide/database/
52
 */
53
abstract class CI_DB_driver {
54
55
	/**
56
	 * Data Source Name / Connect string
57
	 *
58
	 * @var	string
59
	 */
60
	public $dsn;
61
62
	/**
63
	 * Username
64
	 *
65
	 * @var	string
66
	 */
67
	public $username;
68
69
	/**
70
	 * Password
71
	 *
72
	 * @var	string
73
	 */
74
	public $password;
75
76
	/**
77
	 * Hostname
78
	 *
79
	 * @var	string
80
	 */
81
	public $hostname;
82
83
	/**
84
	 * Database name
85
	 *
86
	 * @var	string
87
	 */
88
	public $database;
89
90
	/**
91
	 * Database driver
92
	 *
93
	 * @var	string
94
	 */
95
	public $dbdriver		= 'mysqli';
96
97
	/**
98
	 * Sub-driver
99
	 *
100
	 * @used-by	CI_DB_pdo_driver
101
	 * @var	string
102
	 */
103
	public $subdriver;
104
105
	/**
106
	 * Table prefix
107
	 *
108
	 * @var	string
109
	 */
110
	public $dbprefix		= '';
111
112
	/**
113
	 * Character set
114
	 *
115
	 * @var	string
116
	 */
117
	public $char_set		= 'utf8';
118
119
	/**
120
	 * Collation
121
	 *
122
	 * @var	string
123
	 */
124
	public $dbcollat		= 'utf8_general_ci';
125
126
	/**
127
	 * Encryption flag/data
128
	 *
129
	 * @var	mixed
130
	 */
131
	public $encrypt			= FALSE;
132
133
	/**
134
	 * Swap Prefix
135
	 *
136
	 * @var	string
137
	 */
138
	public $swap_pre		= '';
139
140
	/**
141
	 * Database port
142
	 *
143
	 * @var	int
144
	 */
145
	public $port			= '';
146
147
	/**
148
	 * Persistent connection flag
149
	 *
150
	 * @var	bool
151
	 */
152
	public $pconnect		= FALSE;
153
154
	/**
155
	 * Connection ID
156
	 *
157
	 * @var	object|resource
158
	 */
159
	public $conn_id			= FALSE;
160
161
	/**
162
	 * Result ID
163
	 *
164
	 * @var	object|resource
165
	 */
166
	public $result_id		= FALSE;
167
168
	/**
169
	 * Debug flag
170
	 *
171
	 * Whether to display error messages.
172
	 *
173
	 * @var	bool
174
	 */
175
	public $db_debug		= FALSE;
176
177
	/**
178
	 * Benchmark time
179
	 *
180
	 * @var	int
181
	 */
182
	public $benchmark		= 0;
183
184
	/**
185
	 * Executed queries count
186
	 *
187
	 * @var	int
188
	 */
189
	public $query_count		= 0;
190
191
	/**
192
	 * Bind marker
193
	 *
194
	 * Character used to identify values in a prepared statement.
195
	 *
196
	 * @var	string
197
	 */
198
	public $bind_marker		= '?';
199
200
	/**
201
	 * Save queries flag
202
	 *
203
	 * Whether to keep an in-memory history of queries for debugging purposes.
204
	 *
205
	 * @var	bool
206
	 */
207
	public $save_queries		= TRUE;
208
209
	/**
210
	 * Queries list
211
	 *
212
	 * @see	CI_DB_driver::$save_queries
213
	 * @var	string[]
214
	 */
215
	public $queries			= array();
216
217
	/**
218
	 * Query times
219
	 *
220
	 * A list of times that queries took to execute.
221
	 *
222
	 * @var	array
223
	 */
224
	public $query_times		= array();
225
226
	/**
227
	 * Data cache
228
	 *
229
	 * An internal generic value cache.
230
	 *
231
	 * @var	array
232
	 */
233
	public $data_cache		= array();
234
235
	/**
236
	 * Transaction enabled flag
237
	 *
238
	 * @var	bool
239
	 */
240
	public $trans_enabled		= TRUE;
241
242
	/**
243
	 * Strict transaction mode flag
244
	 *
245
	 * @var	bool
246
	 */
247
	public $trans_strict		= TRUE;
248
249
	/**
250
	 * Transaction depth level
251
	 *
252
	 * @var	int
253
	 */
254
	protected $_trans_depth		= 0;
255
256
	/**
257
	 * Transaction status flag
258
	 *
259
	 * Used with transactions to determine if a rollback should occur.
260
	 *
261
	 * @var	bool
262
	 */
263
	protected $_trans_status	= TRUE;
264
265
	/**
266
	 * Transaction failure flag
267
	 *
268
	 * Used with transactions to determine if a transaction has failed.
269
	 *
270
	 * @var	bool
271
	 */
272
	protected $_trans_failure	= FALSE;
273
274
	/**
275
	 * Cache On flag
276
	 *
277
	 * @var	bool
278
	 */
279
	public $cache_on		= FALSE;
280
281
	/**
282
	 * Cache directory path
283
	 *
284
	 * @var	bool
285
	 */
286
	public $cachedir		= '';
287
288
	/**
289
	 * Cache auto-delete flag
290
	 *
291
	 * @var	bool
292
	 */
293
	public $cache_autodel		= FALSE;
294
295
	/**
296
	 * DB Cache object
297
	 *
298
	 * @see	CI_DB_cache
299
	 * @var	object
300
	 */
301
	public $CACHE;
302
303
	/**
304
	 * Protect identifiers flag
305
	 *
306
	 * @var	bool
307
	 */
308
	protected $_protect_identifiers		= TRUE;
309
310
	/**
311
	 * List of reserved identifiers
312
	 *
313
	 * Identifiers that must NOT be escaped.
314
	 *
315
	 * @var	string[]
316
	 */
317
	protected $_reserved_identifiers	= array('*');
318
319
	/**
320
	 * Identifier escape character
321
	 *
322
	 * @var	string
323
	 */
324
	protected $_escape_char = '"';
325
326
	/**
327
	 * ESCAPE statement string
328
	 *
329
	 * @var	string
330
	 */
331
	protected $_like_escape_str = " ESCAPE '%s' ";
332
333
	/**
334
	 * ESCAPE character
335
	 *
336
	 * @var	string
337
	 */
338
	protected $_like_escape_chr = '!';
339
340
	/**
341
	 * ORDER BY random keyword
342
	 *
343
	 * @var	array
344
	 */
345
	protected $_random_keyword = array('RAND()', 'RAND(%d)');
346
347
	/**
348
	 * COUNT string
349
	 *
350
	 * @used-by	CI_DB_driver::count_all()
351
	 * @used-by	CI_DB_query_builder::count_all_results()
352
	 *
353
	 * @var	string
354
	 */
355
	protected $_count_string = 'SELECT COUNT(*) AS ';
356
357
	// --------------------------------------------------------------------
358
359
	/**
360
	 * Class constructor
361
	 *
362
	 * @param	array	$params
363
	 * @return	void
364
	 */
365
	public function __construct($params)
366
	{
367
		if (is_array($params))
368
		{
369
			foreach ($params as $key => $val)
370
			{
371
				$this->$key = $val;
372
			}
373
		}
374
375
		log_message('info', 'Database Driver Class Initialized');
376
	}
377
378
	// --------------------------------------------------------------------
379
380
	/**
381
	 * Initialize Database Settings
382
	 *
383
	 * @return	bool
384
	 */
385
	public function initialize()
386
	{
387
		/* If an established connection is available, then there's
388
		 * no need to connect and select the database.
389
		 *
390
		 * Depending on the database driver, conn_id can be either
391
		 * boolean TRUE, a resource or an object.
392
		 */
393
		if ($this->conn_id)
394
		{
395
			return TRUE;
396
		}
397
398
		// ----------------------------------------------------------------
399
400
		// Connect to the database and set the connection ID
401
		$this->conn_id = $this->db_connect($this->pconnect);
402
403
		// No connection resource? Check if there is a failover else throw an error
404
		if ( ! $this->conn_id)
405
		{
406
			// Check if there is a failover set
407
			if ( ! empty($this->failover) && is_array($this->failover))
408
			{
409
				// Go over all the failovers
410
				foreach ($this->failover as $failover)
411
				{
412
					// Replace the current settings with those of the failover
413
					foreach ($failover as $key => $val)
414
					{
415
						$this->$key = $val;
416
					}
417
418
					// Try to connect
419
					$this->conn_id = $this->db_connect($this->pconnect);
420
421
					// If a connection is made break the foreach loop
422
					if ($this->conn_id)
423
					{
424
						break;
425
					}
426
				}
427
			}
428
429
			// We still don't have a connection?
430
			if ( ! $this->conn_id)
431
			{
432
				log_message('error', 'Unable to connect to the database');
433
434
				if ($this->db_debug)
435
				{
436
					$this->display_error('db_unable_to_connect');
437
				}
438
439
				return FALSE;
440
			}
441
		}
442
443
		// Now we set the character set and that's all
444
		return $this->db_set_charset($this->char_set);
445
	}
446
447
	// --------------------------------------------------------------------
448
449
	/**
450
	 * DB connect
451
	 *
452
	 * This is just a dummy method that all drivers will override.
453
	 *
454
	 * @return	mixed
455
	 */
456
	public function db_connect()
457
	{
458
		return TRUE;
459
	}
460
461
	// --------------------------------------------------------------------
462
463
	/**
464
	 * Persistent database connection
465
	 *
466
	 * @return	mixed
467
	 */
468
	public function db_pconnect()
469
	{
470
		return $this->db_connect(TRUE);
471
	}
472
473
	// --------------------------------------------------------------------
474
475
	/**
476
	 * Reconnect
477
	 *
478
	 * Keep / reestablish the db connection if no queries have been
479
	 * sent for a length of time exceeding the server's idle timeout.
480
	 *
481
	 * This is just a dummy method to allow drivers without such
482
	 * functionality to not declare it, while others will override it.
483
	 *
484
	 * @return	void
485
	 */
486
	public function reconnect()
487
	{
488
	}
489
490
	// --------------------------------------------------------------------
491
492
	/**
493
	 * Select database
494
	 *
495
	 * This is just a dummy method to allow drivers without such
496
	 * functionality to not declare it, while others will override it.
497
	 *
498
	 * @return	bool
499
	 */
500
	public function db_select()
501
	{
502
		return TRUE;
503
	}
504
505
	// --------------------------------------------------------------------
506
507
	/**
508
	 * Last error
509
	 *
510
	 * @return	array
511
	 */
512
	public function error()
513
	{
514
		return array('code' => NULL, 'message' => NULL);
515
	}
516
517
	// --------------------------------------------------------------------
518
519
	/**
520
	 * Set client character set
521
	 *
522
	 * @param	string
523
	 * @return	bool
524
	 */
525
	public function db_set_charset($charset)
526
	{
527
		if (method_exists($this, '_db_set_charset') && ! $this->_db_set_charset($charset))
528
		{
529
			log_message('error', 'Unable to set database connection charset: '.$charset);
530
531
			if ($this->db_debug)
532
			{
533
				$this->display_error('db_unable_to_set_charset', $charset);
534
			}
535
536
			return FALSE;
537
		}
538
539
		return TRUE;
540
	}
541
542
	// --------------------------------------------------------------------
543
544
	/**
545
	 * The name of the platform in use (mysql, mssql, etc...)
546
	 *
547
	 * @return	string
548
	 */
549
	public function platform()
550
	{
551
		return $this->dbdriver;
552
	}
553
554
	// --------------------------------------------------------------------
555
556
	/**
557
	 * Database version number
558
	 *
559
	 * Returns a string containing the version of the database being used.
560
	 * Most drivers will override this method.
561
	 *
562
	 * @return	string
563
	 */
564
	public function version()
565
	{
566
		if (isset($this->data_cache['version']))
567
		{
568
			return $this->data_cache['version'];
569
		}
570
571
		if (FALSE === ($sql = $this->_version()))
572
		{
573
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->db_debug ? $this-...ted_function') : FALSE; of type string|false adds false to the return on line 573 which is incompatible with the return type documented by CI_DB_driver::version of type string. It seems like you forgot to handle an error condition.
Loading history...
574
		}
575
576
		$query = $this->query($sql)->row();
577
		return $this->data_cache['version'] = $query->ver;
578
	}
579
580
	// --------------------------------------------------------------------
581
582
	/**
583
	 * Version number query string
584
	 *
585
	 * @return	string
586
	 */
587
	protected function _version()
588
	{
589
		return 'SELECT VERSION() AS ver';
590
	}
591
592
	// --------------------------------------------------------------------
593
594
	/**
595
	 * Execute the query
596
	 *
597
	 * Accepts an SQL string as input and returns a result object upon
598
	 * successful execution of a "read" type query. Returns boolean TRUE
599
	 * upon successful execution of a "write" type query. Returns boolean
600
	 * FALSE upon failure, and if the $db_debug variable is set to TRUE
601
	 * will raise an error.
602
	 *
603
	 * @param	string	$sql
604
	 * @param	array	$binds = FALSE		An array of binding data
605
	 * @param	bool	$return_object = NULL
606
	 * @return	mixed
607
	 */
608
	public function query($sql, $binds = FALSE, $return_object = NULL)
609
	{
610
		if ($sql === '')
611
		{
612
			log_message('error', 'Invalid query: '.$sql);
613
			return ($this->db_debug) ? $this->display_error('db_invalid_query') : FALSE;
614
		}
615
		elseif ( ! is_bool($return_object))
616
		{
617
			$return_object = ! $this->is_write_type($sql);
618
		}
619
620
		// Verify table prefix and replace if necessary
621
		if ($this->dbprefix !== '' && $this->swap_pre !== '' && $this->dbprefix !== $this->swap_pre)
622
		{
623
			$sql = preg_replace('/(\W)'.$this->swap_pre.'(\S+?)/', '\\1'.$this->dbprefix.'\\2', $sql);
624
		}
625
626
		// Compile binds if needed
627
		if ($binds !== FALSE)
628
		{
629
			$sql = $this->compile_binds($sql, $binds);
630
		}
631
632
		// Is query caching enabled? If the query is a "read type"
633
		// we will load the caching class and return the previously
634
		// cached query if it exists
635
		if ($this->cache_on === TRUE && $return_object === TRUE && $this->_cache_init())
636
		{
637
			$this->load_rdriver();
638
			if (FALSE !== ($cache = $this->CACHE->read($sql)))
639
			{
640
				return $cache;
641
			}
642
		}
643
644
		// Save the query for debugging
645
		if ($this->save_queries === TRUE)
646
		{
647
			$this->queries[] = $sql;
648
		}
649
650
		// Start the Query Timer
651
		$time_start = microtime(TRUE);
652
653
		// Run the Query
654
		if (FALSE === ($this->result_id = $this->simple_query($sql)))
655
		{
656
			if ($this->save_queries === TRUE)
657
			{
658
				$this->query_times[] = 0;
659
			}
660
661
			// This will trigger a rollback if transactions are being used
662
			if ($this->_trans_depth !== 0)
663
			{
664
				$this->_trans_status = FALSE;
665
			}
666
667
			// Grab the error now, as we might run some additional queries before displaying the error
668
			$error = $this->error();
669
670
			// Log errors
671
			log_message('error', 'Query error: '.$error['message'].' - Invalid query: '.$sql);
672
673
			if ($this->db_debug)
674
			{
675
				// We call this function in order to roll-back queries
676
				// if transactions are enabled. If we don't call this here
677
				// the error message will trigger an exit, causing the
678
				// transactions to remain in limbo.
679
				if ($this->_trans_depth !== 0)
680
				{
681
					do
682
					{
683
						$trans_depth = $this->_trans_depth;
684
						$this->trans_complete();
685
						if ($trans_depth === $this->_trans_depth)
686
						{
687
							log_message('error', 'Database: Failure during an automated transaction commit/rollback!');
688
							break;
689
						}
690
					}
691
					while ($this->_trans_depth !== 0);
692
				}
693
694
				// Display errors
695
				return $this->display_error(array('Error Number: '.$error['code'], $error['message'], $sql));
0 ignored issues
show
array('Error Number: ' ....error['message'], $sql) is of type array<integer,string|nul...":"null","2":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
696
			}
697
698
			return FALSE;
699
		}
700
701
		// Stop and aggregate the query time results
702
		$time_end = microtime(TRUE);
703
		$this->benchmark += $time_end - $time_start;
704
705
		if ($this->save_queries === TRUE)
706
		{
707
			$this->query_times[] = $time_end - $time_start;
708
		}
709
710
		// Increment the query counter
711
		$this->query_count++;
712
713
		// Will we have a result object instantiated? If not - we'll simply return TRUE
714
		if ($return_object !== TRUE)
715
		{
716
			// If caching is enabled we'll auto-cleanup any existing files related to this particular URI
717
			if ($this->cache_on === TRUE && $this->cache_autodel === TRUE && $this->_cache_init())
718
			{
719
				$this->CACHE->delete();
720
			}
721
722
			return TRUE;
723
		}
724
725
		// Load and instantiate the result driver
726
		$driver		= $this->load_rdriver();
727
		$RES		= new $driver($this);
728
729
		// Is query caching enabled? If so, we'll serialize the
730
		// result object and save it to a cache file.
731
		if ($this->cache_on === TRUE && $this->_cache_init())
732
		{
733
			// We'll create a new instance of the result object
734
			// only without the platform specific driver since
735
			// we can't use it with cached data (the query result
736
			// resource ID won't be any good once we've cached the
737
			// result object, so we'll have to compile the data
738
			// and save it)
739
			$CR = new CI_DB_result($this);
740
			$CR->result_object	= $RES->result_object();
741
			$CR->result_array	= $RES->result_array();
742
			$CR->num_rows		= $RES->num_rows();
743
744
			// Reset these since cached objects can not utilize resource IDs.
745
			$CR->conn_id		= NULL;
746
			$CR->result_id		= NULL;
747
748
			$this->CACHE->write($sql, $CR);
749
		}
750
751
		return $RES;
752
	}
753
754
	// --------------------------------------------------------------------
755
756
	/**
757
	 * Load the result drivers
758
	 *
759
	 * @return	string	the name of the result class
760
	 */
761
	public function load_rdriver()
762
	{
763
		$driver = 'CI_DB_'.$this->dbdriver.'_result';
764
765
		if ( ! class_exists($driver, FALSE))
766
		{
767
			require_once(BASEPATH.'database/DB_result.php');
768
			require_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result.php');
769
		}
770
771
		return $driver;
772
	}
773
774
	// --------------------------------------------------------------------
775
776
	/**
777
	 * Simple Query
778
	 * This is a simplified version of the query() function. Internally
779
	 * we only use it when running transaction commands since they do
780
	 * not require all the features of the main query() function.
781
	 *
782
	 * @param	string	the sql query
783
	 * @return	mixed
784
	 */
785
	public function simple_query($sql)
786
	{
787
		if ( ! $this->conn_id)
788
		{
789
			if ( ! $this->initialize())
790
			{
791
				return FALSE;
792
			}
793
		}
794
795
		return $this->_execute($sql);
796
	}
797
798
	// --------------------------------------------------------------------
799
800
	/**
801
	 * Disable Transactions
802
	 * This permits transactions to be disabled at run-time.
803
	 *
804
	 * @return	void
805
	 */
806
	public function trans_off()
807
	{
808
		$this->trans_enabled = FALSE;
809
	}
810
811
	// --------------------------------------------------------------------
812
813
	/**
814
	 * Enable/disable Transaction Strict Mode
815
	 *
816
	 * When strict mode is enabled, if you are running multiple groups of
817
	 * transactions, if one group fails all subsequent groups will be
818
	 * rolled back.
819
	 *
820
	 * If strict mode is disabled, each group is treated autonomously,
821
	 * meaning a failure of one group will not affect any others
822
	 *
823
	 * @param	bool	$mode = TRUE
824
	 * @return	void
825
	 */
826
	public function trans_strict($mode = TRUE)
827
	{
828
		$this->trans_strict = is_bool($mode) ? $mode : TRUE;
829
	}
830
831
	// --------------------------------------------------------------------
832
833
	/**
834
	 * Start Transaction
835
	 *
836
	 * @param	bool	$test_mode = FALSE
837
	 * @return	bool
838
	 */
839
	public function trans_start($test_mode = FALSE)
840
	{
841
		if ( ! $this->trans_enabled)
842
		{
843
			return FALSE;
844
		}
845
846
		return $this->trans_begin($test_mode);
847
	}
848
849
	// --------------------------------------------------------------------
850
851
	/**
852
	 * Complete Transaction
853
	 *
854
	 * @return	bool
855
	 */
856
	public function trans_complete()
857
	{
858
		if ( ! $this->trans_enabled)
859
		{
860
			return FALSE;
861
		}
862
863
		// The query() function will set this flag to FALSE in the event that a query failed
864
		if ($this->_trans_status === FALSE OR $this->_trans_failure === TRUE)
865
		{
866
			$this->trans_rollback();
867
868
			// If we are NOT running in strict mode, we will reset
869
			// the _trans_status flag so that subsequent groups of
870
			// transactions will be permitted.
871
			if ($this->trans_strict === FALSE)
872
			{
873
				$this->_trans_status = TRUE;
874
			}
875
876
			log_message('debug', 'DB Transaction Failure');
877
			return FALSE;
878
		}
879
880
		return $this->trans_commit();
881
	}
882
883
	// --------------------------------------------------------------------
884
885
	/**
886
	 * Lets you retrieve the transaction flag to determine if it has failed
887
	 *
888
	 * @return	bool
889
	 */
890
	public function trans_status()
891
	{
892
		return $this->_trans_status;
893
	}
894
895
	// --------------------------------------------------------------------
896
897
	/**
898
	 * Begin Transaction
899
	 *
900
	 * @param	bool	$test_mode
901
	 * @return	bool
902
	 */
903
	public function trans_begin($test_mode = FALSE)
904
	{
905
		if ( ! $this->trans_enabled)
906
		{
907
			return FALSE;
908
		}
909
		// When transactions are nested we only begin/commit/rollback the outermost ones
910
		elseif ($this->_trans_depth > 0)
911
		{
912
			$this->_trans_depth++;
913
			return TRUE;
914
		}
915
916
		// Reset the transaction failure flag.
917
		// If the $test_mode flag is set to TRUE transactions will be rolled back
918
		// even if the queries produce a successful result.
919
		$this->_trans_failure = ($test_mode === TRUE);
920
921
		if ($this->_trans_begin())
922
		{
923
			$this->_trans_depth++;
924
			return TRUE;
925
		}
926
927
		return FALSE;
928
	}
929
930
	// --------------------------------------------------------------------
931
932
	/**
933
	 * Commit Transaction
934
	 *
935
	 * @return	bool
936
	 */
937 View Code Duplication
	public function trans_commit()
938
	{
939
		if ( ! $this->trans_enabled OR $this->_trans_depth === 0)
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
940
		{
941
			return FALSE;
942
		}
943
		// When transactions are nested we only begin/commit/rollback the outermost ones
944
		elseif ($this->_trans_depth > 1 OR $this->_trans_commit())
945
		{
946
			$this->_trans_depth--;
947
			return TRUE;
948
		}
949
950
		return FALSE;
951
	}
952
953
	// --------------------------------------------------------------------
954
955
	/**
956
	 * Rollback Transaction
957
	 *
958
	 * @return	bool
959
	 */
960 View Code Duplication
	public function trans_rollback()
961
	{
962
		if ( ! $this->trans_enabled OR $this->_trans_depth === 0)
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
963
		{
964
			return FALSE;
965
		}
966
		// When transactions are nested we only begin/commit/rollback the outermost ones
967
		elseif ($this->_trans_depth > 1 OR $this->_trans_rollback())
968
		{
969
			$this->_trans_depth--;
970
			return TRUE;
971
		}
972
973
		return FALSE;
974
	}
975
976
	// --------------------------------------------------------------------
977
978
	/**
979
	 * Compile Bindings
980
	 *
981
	 * @param	string	the sql statement
982
	 * @param	array	an array of bind data
983
	 * @return	string
984
	 */
985
	public function compile_binds($sql, $binds)
986
	{
987
		if (empty($binds) OR empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)
988
		{
989
			return $sql;
990
		}
991
		elseif ( ! is_array($binds))
992
		{
993
			$binds = array($binds);
994
			$bind_count = 1;
995
		}
996
		else
997
		{
998
			// Make sure we're using numeric keys
999
			$binds = array_values($binds);
1000
			$bind_count = count($binds);
1001
		}
1002
1003
		// We'll need the marker length later
1004
		$ml = strlen($this->bind_marker);
1005
1006
		// Make sure not to replace a chunk inside a string that happens to match the bind marker
1007
		if ($c = preg_match_all("/'[^']*'/i", $sql, $matches))
1008
		{
1009
			$c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i',
1010
				str_replace($matches[0],
1011
					str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),
1012
					$sql, $c),
1013
				$matches, PREG_OFFSET_CAPTURE);
1014
1015
			// Bind values' count must match the count of markers in the query
1016
			if ($bind_count !== $c)
1017
			{
1018
				return $sql;
1019
			}
1020
		}
1021
		elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)
1022
		{
1023
			return $sql;
1024
		}
1025
1026
		do
1027
		{
1028
			$c--;
1029
			$escaped_value = $this->escape($binds[$c]);
1030
			if (is_array($escaped_value))
1031
			{
1032
				$escaped_value = '('.implode(',', $escaped_value).')';
1033
			}
1034
			$sql = substr_replace($sql, $escaped_value, $matches[0][$c][1], $ml);
1035
		}
1036
		while ($c !== 0);
1037
1038
		return $sql;
1039
	}
1040
1041
	// --------------------------------------------------------------------
1042
1043
	/**
1044
	 * Determines if a query is a "write" type.
1045
	 *
1046
	 * @param	string	An SQL query string
1047
	 * @return	bool
1048
	 */
1049
	public function is_write_type($sql)
1050
	{
1051
		return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s/i', $sql);
1052
	}
1053
1054
	// --------------------------------------------------------------------
1055
1056
	/**
1057
	 * Calculate the aggregate query elapsed time
1058
	 *
1059
	 * @param	int	The number of decimal places
1060
	 * @return	string
1061
	 */
1062
	public function elapsed_time($decimals = 6)
1063
	{
1064
		return number_format($this->benchmark, $decimals);
1065
	}
1066
1067
	// --------------------------------------------------------------------
1068
1069
	/**
1070
	 * Returns the total number of queries
1071
	 *
1072
	 * @return	int
1073
	 */
1074
	public function total_queries()
1075
	{
1076
		return $this->query_count;
1077
	}
1078
1079
	// --------------------------------------------------------------------
1080
1081
	/**
1082
	 * Returns the last query that was executed
1083
	 *
1084
	 * @return	string
1085
	 */
1086
	public function last_query()
1087
	{
1088
		return end($this->queries);
1089
	}
1090
1091
	// --------------------------------------------------------------------
1092
1093
	/**
1094
	 * "Smart" Escape String
1095
	 *
1096
	 * Escapes data based on type
1097
	 * Sets boolean and null types
1098
	 *
1099
	 * @param	string
1100
	 * @return	mixed
1101
	 */
1102
	public function escape($str)
1103
	{
1104
		if (is_array($str))
1105
		{
1106
			$str = array_map(array(&$this, 'escape'), $str);
1107
			return $str;
1108
		}
1109
		elseif (is_string($str) OR (is_object($str) && method_exists($str, '__toString')))
1110
		{
1111
			return "'".$this->escape_str($str)."'";
1112
		}
1113
		elseif (is_bool($str))
1114
		{
1115
			return ($str === FALSE) ? 0 : 1;
1116
		}
1117
		elseif ($str === NULL)
1118
		{
1119
			return 'NULL';
1120
		}
1121
1122
		return $str;
1123
	}
1124
1125
	// --------------------------------------------------------------------
1126
1127
	/**
1128
	 * Escape String
1129
	 *
1130
	 * @param	string|string[]	$str	Input string
1131
	 * @param	bool	$like	Whether or not the string will be used in a LIKE condition
1132
	 * @return	string
1133
	 */
1134
	public function escape_str($str, $like = FALSE)
1135
	{
1136
		if (is_array($str))
1137
		{
1138
			foreach ($str as $key => $val)
1139
			{
1140
				$str[$key] = $this->escape_str($val, $like);
1141
			}
1142
1143
			return $str;
1144
		}
1145
1146
		$str = $this->_escape_str($str);
1147
1148
		// escape LIKE condition wildcards
1149
		if ($like === TRUE)
1150
		{
1151
			return str_replace(
1152
				array($this->_like_escape_chr, '%', '_'),
1153
				array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
1154
				$str
1155
			);
1156
		}
1157
1158
		return $str;
1159
	}
1160
1161
	// --------------------------------------------------------------------
1162
1163
	/**
1164
	 * Escape LIKE String
1165
	 *
1166
	 * Calls the individual driver for platform
1167
	 * specific escaping for LIKE conditions
1168
	 *
1169
	 * @param	string|string[]
1170
	 * @return	mixed
1171
	 */
1172
	public function escape_like_str($str)
1173
	{
1174
		return $this->escape_str($str, TRUE);
1175
	}
1176
1177
	// --------------------------------------------------------------------
1178
1179
	/**
1180
	 * Platform-dependant string escape
1181
	 *
1182
	 * @param	string
1183
	 * @return	string
1184
	 */
1185
	protected function _escape_str($str)
1186
	{
1187
		return str_replace("'", "''", remove_invisible_characters($str));
1188
	}
1189
1190
	// --------------------------------------------------------------------
1191
1192
	/**
1193
	 * Primary
1194
	 *
1195
	 * Retrieves the primary key. It assumes that the row in the first
1196
	 * position is the primary key
1197
	 *
1198
	 * @param	string	$table	Table name
1199
	 * @return	string
1200
	 */
1201
	public function primary($table)
1202
	{
1203
		$fields = $this->list_fields($table);
1204
		return is_array($fields) ? current($fields) : FALSE;
1205
	}
1206
1207
	// --------------------------------------------------------------------
1208
1209
	/**
1210
	 * "Count All" query
1211
	 *
1212
	 * Generates a platform-specific query string that counts all records in
1213
	 * the specified database
1214
	 *
1215
	 * @param	string
1216
	 * @return	int
1217
	 */
1218
	public function count_all($table = '')
1219
	{
1220
		if ($table === '')
1221
		{
1222
			return 0;
1223
		}
1224
1225
		$query = $this->query($this->_count_string.$this->escape_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
1226
		if ($query->num_rows() === 0)
1227
		{
1228
			return 0;
1229
		}
1230
1231
		$query = $query->row();
1232
		$this->_reset_select();
1233
		return (int) $query->numrows;
1234
	}
1235
1236
	// --------------------------------------------------------------------
1237
1238
	/**
1239
	 * Returns an array of table names
1240
	 *
1241
	 * @param	string	$constrain_by_prefix = FALSE
1242
	 * @return	array
1243
	 */
1244
	public function list_tables($constrain_by_prefix = FALSE)
1245
	{
1246
		// Is there a cached result?
1247
		if (isset($this->data_cache['table_names']))
1248
		{
1249
			return $this->data_cache['table_names'];
1250
		}
1251
1252
		if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))
1253
		{
1254
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
1255
		}
1256
1257
		$this->data_cache['table_names'] = array();
1258
		$query = $this->query($sql);
1259
1260 View Code Duplication
		foreach ($query->result_array() as $row)
0 ignored issues
show
This code seems to be duplicated across 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...
1261
		{
1262
			// Do we know from which column to get the table name?
1263
			if ( ! isset($key))
1264
			{
1265
				if (isset($row['table_name']))
1266
				{
1267
					$key = 'table_name';
1268
				}
1269
				elseif (isset($row['TABLE_NAME']))
1270
				{
1271
					$key = 'TABLE_NAME';
1272
				}
1273
				else
1274
				{
1275
					/* We have no other choice but to just get the first element's key.
1276
					 * Due to array_shift() accepting its argument by reference, if
1277
					 * E_STRICT is on, this would trigger a warning. So we'll have to
1278
					 * assign it first.
1279
					 */
1280
					$key = array_keys($row);
1281
					$key = array_shift($key);
1282
				}
1283
			}
1284
1285
			$this->data_cache['table_names'][] = $row[$key];
1286
		}
1287
1288
		return $this->data_cache['table_names'];
1289
	}
1290
1291
	// --------------------------------------------------------------------
1292
1293
	/**
1294
	 * Determine if a particular table exists
1295
	 *
1296
	 * @param	string	$table_name
1297
	 * @return	bool
1298
	 */
1299
	public function table_exists($table_name)
1300
	{
1301
		return in_array($this->protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables());
1302
	}
1303
1304
	// --------------------------------------------------------------------
1305
1306
	/**
1307
	 * Fetch Field Names
1308
	 *
1309
	 * @param	string	$table	Table name
1310
	 * @return	array
1311
	 */
1312
	public function list_fields($table)
1313
	{
1314
		// Is there a cached result?
1315
		if (isset($this->data_cache['field_names'][$table]))
1316
		{
1317
			return $this->data_cache['field_names'][$table];
1318
		}
1319
1320
		if (FALSE === ($sql = $this->_list_columns($table)))
1321
		{
1322
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
1323
		}
1324
1325
		$query = $this->query($sql);
1326
		$this->data_cache['field_names'][$table] = array();
1327
1328 View Code Duplication
		foreach ($query->result_array() as $row)
0 ignored issues
show
This code seems to be duplicated across 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...
1329
		{
1330
			// Do we know from where to get the column's name?
1331
			if ( ! isset($key))
1332
			{
1333
				if (isset($row['column_name']))
1334
				{
1335
					$key = 'column_name';
1336
				}
1337
				elseif (isset($row['COLUMN_NAME']))
1338
				{
1339
					$key = 'COLUMN_NAME';
1340
				}
1341
				else
1342
				{
1343
					// We have no other choice but to just get the first element's key.
1344
					$key = key($row);
1345
				}
1346
			}
1347
1348
			$this->data_cache['field_names'][$table][] = $row[$key];
1349
		}
1350
1351
		return $this->data_cache['field_names'][$table];
1352
	}
1353
1354
	// --------------------------------------------------------------------
1355
1356
	/**
1357
	 * Determine if a particular field exists
1358
	 *
1359
	 * @param	string
1360
	 * @param	string
1361
	 * @return	bool
1362
	 */
1363
	public function field_exists($field_name, $table_name)
1364
	{
1365
		return in_array($field_name, $this->list_fields($table_name));
1366
	}
1367
1368
	// --------------------------------------------------------------------
1369
1370
	/**
1371
	 * Returns an object with field data
1372
	 *
1373
	 * @param	string	$table	the table name
1374
	 * @return	array
1375
	 */
1376
	public function field_data($table)
1377
	{
1378
		$query = $this->query($this->_field_data($this->protect_identifiers($table, TRUE, NULL, FALSE)));
1379
		return ($query) ? $query->field_data() : FALSE;
1380
	}
1381
1382
	// --------------------------------------------------------------------
1383
1384
	/**
1385
	 * Escape the SQL Identifiers
1386
	 *
1387
	 * This function escapes column and table names
1388
	 *
1389
	 * @param	mixed
1390
	 * @return	mixed
1391
	 */
1392
	public function escape_identifiers($item)
1393
	{
1394
		if ($this->_escape_char === '' OR empty($item) OR in_array($item, $this->_reserved_identifiers))
1395
		{
1396
			return $item;
1397
		}
1398
		elseif (is_array($item))
1399
		{
1400
			foreach ($item as $key => $value)
1401
			{
1402
				$item[$key] = $this->escape_identifiers($value);
1403
			}
1404
1405
			return $item;
1406
		}
1407
		// Avoid breaking functions and literal values inside queries
1408
		elseif (ctype_digit($item) OR $item[0] === "'" OR ($this->_escape_char !== '"' && $item[0] === '"') OR strpos($item, '(') !== FALSE)
1409
		{
1410
			return $item;
1411
		}
1412
1413
		static $preg_ec = array();
1414
1415
		if (empty($preg_ec))
1416
		{
1417
			if (is_array($this->_escape_char))
1418
			{
1419
				$preg_ec = array(
1420
					preg_quote($this->_escape_char[0], '/'),
1421
					preg_quote($this->_escape_char[1], '/'),
1422
					$this->_escape_char[0],
1423
					$this->_escape_char[1]
1424
				);
1425
			}
1426
			else
1427
			{
1428
				$preg_ec[0] = $preg_ec[1] = preg_quote($this->_escape_char, '/');
1429
				$preg_ec[2] = $preg_ec[3] = $this->_escape_char;
1430
			}
1431
		}
1432
1433
		foreach ($this->_reserved_identifiers as $id)
1434
		{
1435
			if (strpos($item, '.'.$id) !== FALSE)
1436
			{
1437
				return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?\./i', $preg_ec[2].'$1'.$preg_ec[3].'.', $item);
1438
			}
1439
		}
1440
1441
		return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?(\.)?/i', $preg_ec[2].'$1'.$preg_ec[3].'$2', $item);
1442
	}
1443
1444
	// --------------------------------------------------------------------
1445
1446
	/**
1447
	 * Generate an insert string
1448
	 *
1449
	 * @param	string	the table upon which the query will be performed
1450
	 * @param	array	an associative array data of key/values
1451
	 * @return	string
1452
	 */
1453
	public function insert_string($table, $data)
1454
	{
1455
		$fields = $values = array();
1456
1457
		foreach ($data as $key => $val)
1458
		{
1459
			$fields[] = $this->escape_identifiers($key);
1460
			$values[] = $this->escape($val);
1461
		}
1462
1463
		return $this->_insert($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);
1464
	}
1465
1466
	// --------------------------------------------------------------------
1467
1468
	/**
1469
	 * Insert statement
1470
	 *
1471
	 * Generates a platform-specific insert string from the supplied data
1472
	 *
1473
	 * @param	string	the table name
1474
	 * @param	array	the insert keys
1475
	 * @param	array	the insert values
1476
	 * @return	string
1477
	 */
1478
	protected function _insert($table, $keys, $values)
1479
	{
1480
		return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
1481
	}
1482
1483
	// --------------------------------------------------------------------
1484
1485
	/**
1486
	 * Generate an update string
1487
	 *
1488
	 * @param	string	the table upon which the query will be performed
1489
	 * @param	array	an associative array data of key/values
1490
	 * @param	mixed	the "where" statement
1491
	 * @return	string
1492
	 */
1493
	public function update_string($table, $data, $where)
1494
	{
1495
		if (empty($where))
1496
		{
1497
			return FALSE;
1498
		}
1499
1500
		$this->where($where);
1501
1502
		$fields = array();
1503
		foreach ($data as $key => $val)
1504
		{
1505
			$fields[$this->protect_identifiers($key)] = $this->escape($val);
1506
		}
1507
1508
		$sql = $this->_update($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields);
1509
		$this->_reset_write();
1510
		return $sql;
1511
	}
1512
1513
	// --------------------------------------------------------------------
1514
1515
	/**
1516
	 * Update statement
1517
	 *
1518
	 * Generates a platform-specific update string from the supplied data
1519
	 *
1520
	 * @param	string	the table name
1521
	 * @param	array	the update data
1522
	 * @return	string
1523
	 */
1524
	protected function _update($table, $values)
1525
	{
1526
		foreach ($values as $key => $val)
1527
		{
1528
			$valstr[] = $key.' = '.$val;
1529
		}
1530
1531
		return 'UPDATE '.$table.' SET '.implode(', ', $valstr)
1532
			.$this->_compile_wh('qb_where')
1533
			.$this->_compile_order_by()
1534
			.($this->qb_limit ? ' LIMIT '.$this->qb_limit : '');
1535
	}
1536
1537
	// --------------------------------------------------------------------
1538
1539
	/**
1540
	 * Tests whether the string has an SQL operator
1541
	 *
1542
	 * @param	string
1543
	 * @return	bool
1544
	 */
1545
	protected function _has_operator($str)
1546
	{
1547
		return (bool) preg_match('/(<|>|!|=|\sIS NULL|\sIS NOT NULL|\sEXISTS|\sBETWEEN|\sLIKE|\sIN\s*\(|\s)/i', trim($str));
1548
	}
1549
1550
	// --------------------------------------------------------------------
1551
1552
	/**
1553
	 * Returns the SQL string operator
1554
	 *
1555
	 * @param	string
1556
	 * @return	string
1557
	 */
1558
	protected function _get_operator($str)
1559
	{
1560
		static $_operators;
1561
1562
		if (empty($_operators))
1563
		{
1564
			$_les = ($this->_like_escape_str !== '')
1565
				? '\s+'.preg_quote(trim(sprintf($this->_like_escape_str, $this->_like_escape_chr)), '/')
1566
				: '';
1567
			$_operators = array(
1568
				'\s*(?:<|>|!)?=\s*',             // =, <=, >=, !=
1569
				'\s*<>?\s*',                     // <, <>
1570
				'\s*>\s*',                       // >
1571
				'\s+IS NULL',                    // IS NULL
1572
				'\s+IS NOT NULL',                // IS NOT NULL
1573
				'\s+EXISTS\s*\([^\)]+\)',        // EXISTS(sql)
1574
				'\s+NOT EXISTS\s*\([^\)]+\)',    // NOT EXISTS(sql)
1575
				'\s+BETWEEN\s+',                 // BETWEEN value AND value
1576
				'\s+IN\s*\([^\)]+\)',            // IN(list)
1577
				'\s+NOT IN\s*\([^\)]+\)',        // NOT IN (list)
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1578
				'\s+LIKE\s+\S.*('.$_les.')?',    // LIKE 'expr'[ ESCAPE '%s']
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1579
				'\s+NOT LIKE\s+\S.*('.$_les.')?' // NOT LIKE 'expr'[ ESCAPE '%s']
1580
			);
1581
1582
		}
1583
1584
		return preg_match('/'.implode('|', $_operators).'/i', $str, $match)
1585
			? $match[0] : FALSE;
1586
	}
1587
1588
	// --------------------------------------------------------------------
1589
1590
	/**
1591
	 * Enables a native PHP function to be run, using a platform agnostic wrapper.
1592
	 *
1593
	 * @param	string	$function	Function name
1594
	 * @return	mixed
1595
	 */
1596
	public function call_function($function)
1597
	{
1598
		$driver = ($this->dbdriver === 'postgre') ? 'pg_' : $this->dbdriver.'_';
1599
1600
		if (FALSE === strpos($driver, $function))
1601
		{
1602
			$function = $driver.$function;
1603
		}
1604
1605
		if ( ! function_exists($function))
1606
		{
1607
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
1608
		}
1609
1610
		return (func_num_args() > 1)
1611
			? call_user_func_array($function, array_slice(func_get_args(), 1))
1612
			: call_user_func($function);
1613
	}
1614
1615
	// --------------------------------------------------------------------
1616
1617
	/**
1618
	 * Set Cache Directory Path
1619
	 *
1620
	 * @param	string	the path to the cache directory
1621
	 * @return	void
1622
	 */
1623
	public function cache_set_path($path = '')
1624
	{
1625
		$this->cachedir = $path;
1626
	}
1627
1628
	// --------------------------------------------------------------------
1629
1630
	/**
1631
	 * Enable Query Caching
1632
	 *
1633
	 * @return	bool	cache_on value
1634
	 */
1635
	public function cache_on()
1636
	{
1637
		return $this->cache_on = TRUE;
1638
	}
1639
1640
	// --------------------------------------------------------------------
1641
1642
	/**
1643
	 * Disable Query Caching
1644
	 *
1645
	 * @return	bool	cache_on value
1646
	 */
1647
	public function cache_off()
1648
	{
1649
		return $this->cache_on = FALSE;
1650
	}
1651
1652
	// --------------------------------------------------------------------
1653
1654
	/**
1655
	 * Delete the cache files associated with a particular URI
1656
	 *
1657
	 * @param	string	$segment_one = ''
1658
	 * @param	string	$segment_two = ''
1659
	 * @return	bool
1660
	 */
1661
	public function cache_delete($segment_one = '', $segment_two = '')
1662
	{
1663
		return $this->_cache_init()
1664
			? $this->CACHE->delete($segment_one, $segment_two)
1665
			: FALSE;
1666
	}
1667
1668
	// --------------------------------------------------------------------
1669
1670
	/**
1671
	 * Delete All cache files
1672
	 *
1673
	 * @return	bool
1674
	 */
1675
	public function cache_delete_all()
1676
	{
1677
		return $this->_cache_init()
1678
			? $this->CACHE->delete_all()
1679
			: FALSE;
1680
	}
1681
1682
	// --------------------------------------------------------------------
1683
1684
	/**
1685
	 * Initialize the Cache Class
1686
	 *
1687
	 * @return	bool
1688
	 */
1689
	protected function _cache_init()
1690
	{
1691
		if ( ! class_exists('CI_DB_Cache', FALSE))
1692
		{
1693
			require_once(BASEPATH.'database/DB_cache.php');
1694
		}
1695
		elseif (is_object($this->CACHE))
1696
		{
1697
			return TRUE;
1698
		}
1699
1700
		$this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects
1701
		return TRUE;
1702
	}
1703
1704
	// --------------------------------------------------------------------
1705
1706
	/**
1707
	 * Close DB Connection
1708
	 *
1709
	 * @return	void
1710
	 */
1711
	public function close()
1712
	{
1713
		if ($this->conn_id)
1714
		{
1715
			$this->_close();
1716
			$this->conn_id = FALSE;
0 ignored issues
show
Documentation Bug introduced by
It seems like FALSE of type false is incompatible with the declared type object|resource of property $conn_id.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1717
		}
1718
	}
1719
1720
	// --------------------------------------------------------------------
1721
1722
	/**
1723
	 * Close DB Connection
1724
	 *
1725
	 * This method would be overridden by most of the drivers.
1726
	 *
1727
	 * @return	void
1728
	 */
1729
	protected function _close()
1730
	{
1731
		$this->conn_id = FALSE;
0 ignored issues
show
Documentation Bug introduced by
It seems like FALSE of type false is incompatible with the declared type object|resource of property $conn_id.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1732
	}
1733
1734
	// --------------------------------------------------------------------
1735
1736
	/**
1737
	 * Display an error message
1738
	 *
1739
	 * @param	string	the error message
1740
	 * @param	string	any "swap" values
1741
	 * @param	bool	whether to localize the message
1742
	 * @return	string	sends the application/views/errors/error_db.php template
1743
	 */
1744
	public function display_error($error = '', $swap = '', $native = FALSE)
1745
	{
1746
		$LANG =& load_class('Lang', 'core');
1747
		$LANG->load('db');
1748
1749
		$heading = $LANG->line('db_error_heading');
1750
1751
		if ($native === TRUE)
1752
		{
1753
			$message = (array) $error;
1754
		}
1755
		else
1756
		{
1757
			$message = is_array($error) ? $error : array(str_replace('%s', $swap, $LANG->line($error)));
1758
		}
1759
1760
		// Find the most likely culprit of the error by going through
1761
		// the backtrace until the source file is no longer in the
1762
		// database folder.
1763
		$trace = debug_backtrace();
1764
		foreach ($trace as $call)
1765
		{
1766
			if (isset($call['file'], $call['class']))
1767
			{
1768
				// We'll need this on Windows, as APPPATH and BASEPATH will always use forward slashes
1769
				if (DIRECTORY_SEPARATOR !== '/')
1770
				{
1771
					$call['file'] = str_replace('\\', '/', $call['file']);
1772
				}
1773
1774
				if (strpos($call['file'], BASEPATH.'database') === FALSE && strpos($call['class'], 'Loader') === FALSE)
1775
				{
1776
					// Found it - use a relative path for safety
1777
					$message[] = 'Filename: '.str_replace(array(APPPATH, BASEPATH), '', $call['file']);
1778
					$message[] = 'Line Number: '.$call['line'];
1779
					break;
1780
				}
1781
			}
1782
		}
1783
1784
		$error =& load_class('Exceptions', 'core');
1785
		echo $error->show_error($heading, $message, 'error_db');
1786
		exit(8); // EXIT_DATABASE
1787
	}
1788
1789
	// --------------------------------------------------------------------
1790
1791
	/**
1792
	 * Protect Identifiers
1793
	 *
1794
	 * This function is used extensively by the Query Builder class, and by
1795
	 * a couple functions in this class.
1796
	 * It takes a column or table name (optionally with an alias) and inserts
1797
	 * the table prefix onto it. Some logic is necessary in order to deal with
1798
	 * column names that include the path. Consider a query like this:
1799
	 *
1800
	 * SELECT * FROM hostname.database.table.column AS c FROM hostname.database.table
1801
	 *
1802
	 * Or a query with aliasing:
1803
	 *
1804
	 * SELECT m.member_id, m.member_name FROM members AS m
1805
	 *
1806
	 * Since the column name can include up to four segments (host, DB, table, column)
1807
	 * or also have an alias prefix, we need to do a bit of work to figure this out and
1808
	 * insert the table prefix (if it exists) in the proper position, and escape only
1809
	 * the correct identifiers.
1810
	 *
1811
	 * @param	string
1812
	 * @param	bool
1813
	 * @param	mixed
1814
	 * @param	bool
1815
	 * @return	string
1816
	 */
1817
	public function protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE)
1818
	{
1819
		if ( ! is_bool($protect_identifiers))
1820
		{
1821
			$protect_identifiers = $this->_protect_identifiers;
1822
		}
1823
1824
		if (is_array($item))
1825
		{
1826
			$escaped_array = array();
1827
			foreach ($item as $k => $v)
1828
			{
1829
				$escaped_array[$this->protect_identifiers($k)] = $this->protect_identifiers($v, $prefix_single, $protect_identifiers, $field_exists);
1830
			}
1831
1832
			return $escaped_array;
1833
		}
1834
1835
		// This is basically a bug fix for queries that use MAX, MIN, etc.
1836
		// If a parenthesis is found we know that we do not need to
1837
		// escape the data or add a prefix. There's probably a more graceful
1838
		// way to deal with this, but I'm not thinking of it -- Rick
1839
		//
1840
		// Added exception for single quotes as well, we don't want to alter
1841
		// literal strings. -- Narf
1842
		if (strcspn($item, "()'") !== strlen($item))
1843
		{
1844
			return $item;
1845
		}
1846
1847
		// Convert tabs or multiple spaces into single spaces
1848
		$item = preg_replace('/\s+/', ' ', trim($item));
1849
1850
		// If the item has an alias declaration we remove it and set it aside.
1851
		// Note: strripos() is used in order to support spaces in table names
1852
		if ($offset = strripos($item, ' AS '))
1853
		{
1854
			$alias = ($protect_identifiers)
1855
				? substr($item, $offset, 4).$this->escape_identifiers(substr($item, $offset + 4))
1856
				: substr($item, $offset);
1857
			$item = substr($item, 0, $offset);
1858
		}
1859
		elseif ($offset = strrpos($item, ' '))
1860
		{
1861
			$alias = ($protect_identifiers)
1862
				? ' '.$this->escape_identifiers(substr($item, $offset + 1))
1863
				: substr($item, $offset);
1864
			$item = substr($item, 0, $offset);
1865
		}
1866
		else
1867
		{
1868
			$alias = '';
1869
		}
1870
1871
		// Break the string apart if it contains periods, then insert the table prefix
1872
		// in the correct location, assuming the period doesn't indicate that we're dealing
1873
		// with an alias. While we're at it, we will escape the components
1874
		if (strpos($item, '.') !== FALSE)
1875
		{
1876
			$parts = explode('.', $item);
1877
1878
			// Does the first segment of the exploded item match
1879
			// one of the aliases previously identified? If so,
1880
			// we have nothing more to do other than escape the item
1881
			//
1882
			// NOTE: The ! empty() condition prevents this method
1883
			//       from breaking when QB isn't enabled.
1884
			if ( ! empty($this->qb_aliased_tables) && in_array($parts[0], $this->qb_aliased_tables))
1885
			{
1886
				if ($protect_identifiers === TRUE)
1887
				{
1888
					foreach ($parts as $key => $val)
1889
					{
1890
						if ( ! in_array($val, $this->_reserved_identifiers))
1891
						{
1892
							$parts[$key] = $this->escape_identifiers($val);
1893
						}
1894
					}
1895
1896
					$item = implode('.', $parts);
1897
				}
1898
1899
				return $item.$alias;
1900
			}
1901
1902
			// Is there a table prefix defined in the config file? If not, no need to do anything
1903
			if ($this->dbprefix !== '')
1904
			{
1905
				// We now add the table prefix based on some logic.
1906
				// Do we have 4 segments (hostname.database.table.column)?
1907
				// If so, we add the table prefix to the column name in the 3rd segment.
1908
				if (isset($parts[3]))
1909
				{
1910
					$i = 2;
1911
				}
1912
				// Do we have 3 segments (database.table.column)?
1913
				// If so, we add the table prefix to the column name in 2nd position
1914
				elseif (isset($parts[2]))
1915
				{
1916
					$i = 1;
1917
				}
1918
				// Do we have 2 segments (table.column)?
1919
				// If so, we add the table prefix to the column name in 1st segment
1920
				else
1921
				{
1922
					$i = 0;
1923
				}
1924
1925
				// This flag is set when the supplied $item does not contain a field name.
1926
				// This can happen when this function is being called from a JOIN.
1927
				if ($field_exists === FALSE)
1928
				{
1929
					$i++;
1930
				}
1931
1932
				// Verify table prefix and replace if necessary
1933
				if ($this->swap_pre !== '' && strpos($parts[$i], $this->swap_pre) === 0)
1934
				{
1935
					$parts[$i] = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $parts[$i]);
1936
				}
1937
				// We only add the table prefix if it does not already exist
1938
				elseif (strpos($parts[$i], $this->dbprefix) !== 0)
1939
				{
1940
					$parts[$i] = $this->dbprefix.$parts[$i];
1941
				}
1942
1943
				// Put the parts back together
1944
				$item = implode('.', $parts);
1945
			}
1946
1947
			if ($protect_identifiers === TRUE)
1948
			{
1949
				$item = $this->escape_identifiers($item);
1950
			}
1951
1952
			return $item.$alias;
1953
		}
1954
1955
		// Is there a table prefix? If not, no need to insert it
1956
		if ($this->dbprefix !== '')
1957
		{
1958
			// Verify table prefix and replace if necessary
1959
			if ($this->swap_pre !== '' && strpos($item, $this->swap_pre) === 0)
1960
			{
1961
				$item = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $item);
1962
			}
1963
			// Do we prefix an item with no segments?
1964
			elseif ($prefix_single === TRUE && strpos($item, $this->dbprefix) !== 0)
1965
			{
1966
				$item = $this->dbprefix.$item;
1967
			}
1968
		}
1969
1970
		if ($protect_identifiers === TRUE && ! in_array($item, $this->_reserved_identifiers))
1971
		{
1972
			$item = $this->escape_identifiers($item);
1973
		}
1974
1975
		return $item.$alias;
1976
	}
1977
1978
	// --------------------------------------------------------------------
1979
1980
	/**
1981
	 * Dummy method that allows Query Builder class to be disabled
1982
	 * and keep count_all() working.
1983
	 *
1984
	 * @return	void
1985
	 */
1986
	protected function _reset_select()
1987
	{
1988
	}
1989
1990
}
1991