Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
driver.php
Aller à la documentation de ce fichier.
1 <?php
2 /**
3  * @package Joomla.Platform
4  * @subpackage Database
5  *
6  * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
7  * @license GNU General Public License version 2 or later; see LICENSE
8  */
9 
10 defined('JPATH_PLATFORM') or die;
11 
12 /**
13  * Joomla Platform Database Interface
14  *
15  * @package Joomla.Platform
16  * @subpackage Database
17  * @since 11.2
18  */
20 {
21  /**
22  * Test to see if the connector is available.
23  *
24  * @return boolean True on success, false otherwise.
25  *
26  * @since 11.2
27  */
28  public static function isSupported();
29 }
30 
31 /**
32  * Joomla Platform Database Driver Class
33  *
34  * @package Joomla.Platform
35  * @subpackage Database
36  * @since 12.1
37  *
38  * @method string q() q($text, $escape = true) Alias for quote method
39  * @method string qn() qn($name, $as = null) Alias for quoteName method
40  */
41 abstract class JDatabaseDriver extends JDatabase implements JDatabaseInterface
42 {
43  /**
44  * The name of the database.
45  *
46  * @var string
47  * @since 11.4
48  */
49  private $_database;
50 
51  /**
52  * The name of the database driver.
53  *
54  * @var string
55  * @since 11.1
56  */
57  public $name;
58 
59  /**
60  * @var resource The database connection resource.
61  * @since 11.1
62  */
63  protected $connection;
64 
65  /**
66  * @var integer The number of SQL statements executed by the database driver.
67  * @since 11.1
68  */
69  protected $count = 0;
70 
71  /**
72  * @var resource The database connection cursor from the last query.
73  * @since 11.1
74  */
75  protected $cursor;
76 
77  /**
78  * @var boolean The database driver debugging state.
79  * @since 11.1
80  */
81  protected $debug = false;
82 
83  /**
84  * @var integer The affected row limit for the current SQL statement.
85  * @since 11.1
86  */
87  protected $limit = 0;
88 
89  /**
90  * @var array The log of executed SQL statements by the database driver.
91  * @since 11.1
92  */
93  protected $log = array();
94 
95  /**
96  * @var array The log of executed SQL statements timings (start and stop microtimes) by the database driver.
97  * @since CMS 3.1.2
98  */
99  protected $timings = array();
100 
101  /**
102  * @var array The log of executed SQL statements timings (start and stop microtimes) by the database driver.
103  * @since CMS 3.1.2
104  */
105  protected $callStacks = array();
106 
107  /**
108  * @var string The character(s) used to quote SQL statement names such as table names or field names,
109  * etc. The child classes should define this as necessary. If a single character string the
110  * same character is used for both sides of the quoted name, else the first character will be
111  * used for the opening quote and the second for the closing quote.
112  * @since 11.1
113  */
114  protected $nameQuote;
115 
116  /**
117  * @var string The null or zero representation of a timestamp for the database driver. This should be
118  * defined in child classes to hold the appropriate value for the engine.
119  * @since 11.1
120  */
121  protected $nullDate;
122 
123  /**
124  * @var integer The affected row offset to apply for the current SQL statement.
125  * @since 11.1
126  */
127  protected $offset = 0;
128 
129  /**
130  * @var array Passed in upon instantiation and saved.
131  * @since 11.1
132  */
133  protected $options;
134 
135  /**
136  * @var mixed The current SQL statement to execute.
137  * @since 11.1
138  */
139  protected $sql;
140 
141  /**
142  * @var string The common database table prefix.
143  * @since 11.1
144  */
145  protected $tablePrefix;
146 
147  /**
148  * @var boolean True if the database engine supports UTF-8 character encoding.
149  * @since 11.1
150  */
151  protected $utf = true;
152 
153  /**
154  * @var integer The database error number
155  * @since 11.1
156  * @deprecated 12.1
157  */
158  protected $errorNum = 0;
159 
160  /**
161  * @var string The database error message
162  * @since 11.1
163  * @deprecated 12.1
164  */
165  protected $errorMsg;
166 
167  /**
168  * @var array JDatabaseDriver instances container.
169  * @since 11.1
170  */
171  protected static $instances = array();
172 
173  /**
174  * @var string The minimum supported database version.
175  * @since 12.1
176  */
177  protected static $dbMinimum;
178 
179  /**
180  * @var integer The depth of the current transaction.
181  * @since 12.3
182  */
183  protected $transactionDepth = 0;
184 
185  /**
186  * @var callable[] List of callables to call just before disconnecting database
187  * @since CMS 3.1.2
188  */
189  protected $disconnectHandlers = array();
190 
191  /**
192  * Get a list of available database connectors. The list will only be populated with connectors that both
193  * the class exists and the static test method returns true. This gives us the ability to have a multitude
194  * of connector classes that are self-aware as to whether or not they are able to be used on a given system.
195  *
196  * @return array An array of available database connectors.
197  *
198  * @since 11.1
199  */
200  public static function getConnectors()
201  {
202  $connectors = array();
203 
204  // Get an iterator and loop trough the driver classes.
205  $iterator = new DirectoryIterator(__DIR__ . '/driver');
206 
207  foreach ($iterator as $file)
208  {
209  $fileName = $file->getFilename();
210 
211  // Only load for php files.
212  // Note: DirectoryIterator::getExtension only available PHP >= 5.3.6
213  if (!$file->isFile() || substr($fileName, strrpos($fileName, '.') + 1) != 'php')
214  {
215  continue;
216  }
217 
218  // Derive the class name from the type.
219  $class = str_ireplace('.php', '', 'JDatabaseDriver' . ucfirst(trim($fileName)));
220 
221  // If the class doesn't exist we have nothing left to do but look at the next type. We did our best.
222  if (!class_exists($class))
223  {
224  continue;
225  }
226 
227  // Sweet! Our class exists, so now we just need to know if it passes its test method.
228  if ($class::isSupported())
229  {
230  // Connector names should not have file extensions.
231  $connectors[] = str_ireplace('.php', '', $fileName);
232  }
233  }
234 
235  return $connectors;
236  }
237 
238  /**
239  * Method to return a JDatabaseDriver instance based on the given options. There are three global options and then
240  * the rest are specific to the database driver. The 'driver' option defines which JDatabaseDriver class is
241  * used for the connection -- the default is 'mysqli'. The 'database' option determines which database is to
242  * be used for the connection. The 'select' option determines whether the connector should automatically select
243  * the chosen database.
244  *
245  * Instances are unique to the given options and new objects are only created when a unique options array is
246  * passed into the method. This ensures that we don't end up with unnecessary database connection resources.
247  *
248  * @param array $options Parameters to be passed to the database driver.
249  *
250  * @return JDatabaseDriver A database object.
251  *
252  * @since 11.1
253  * @throws RuntimeException
254  */
255  public static function getInstance($options = array())
256  {
257  // Sanitize the database connector options.
258  $options['driver'] = (isset($options['driver'])) ? preg_replace('/[^A-Z0-9_\.-]/i', '', $options['driver']) : 'mysqli';
259  $options['database'] = (isset($options['database'])) ? $options['database'] : null;
260  $options['select'] = (isset($options['select'])) ? $options['select'] : true;
261 
262  // Get the options signature for the database connector.
263  $signature = md5(serialize($options));
264 
265  // If we already have a database connector instance for these options then just use that.
266  if (empty(self::$instances[$signature]))
267  {
268 
269  // Derive the class name from the driver.
270  $class = 'JDatabaseDriver' . ucfirst(strtolower($options['driver']));
271 
272  // If the class still doesn't exist we have nothing left to do but throw an exception. We did our best.
273  if (!class_exists($class))
274  {
275  throw new RuntimeException(sprintf('Unable to load Database Driver: %s', $options['driver']));
276  }
277 
278  // Create our new JDatabaseDriver connector based on the options given.
279  try
280  {
281  $instance = new $class($options);
282  }
283  catch (RuntimeException $e)
284  {
285  throw new RuntimeException(sprintf('Unable to connect to the Database: %s', $e->getMessage()));
286  }
287 
288  // Set the new connector to the global instances based on signature.
289  self::$instances[$signature] = $instance;
290  }
291 
292  return self::$instances[$signature];
293  }
294 
295  /**
296  * Splits a string of multiple queries into an array of individual queries.
297  *
298  * @param string $sql Input SQL string with which to split into individual queries.
299  *
300  * @return array The queries from the input string separated into an array.
301  *
302  * @since 11.1
303  */
304  public static function splitSql($sql)
305  {
306  $start = 0;
307  $open = false;
308  $char = '';
309  $end = strlen($sql);
310  $queries = array();
311 
312  for ($i = 0; $i < $end; $i++)
313  {
314  $current = substr($sql, $i, 1);
315 
316  if (($current == '"' || $current == '\''))
317  {
318  $n = 2;
319 
320  while (substr($sql, $i - $n + 1, 1) == '\\' && $n < $i)
321  {
322  $n++;
323  }
324 
325  if ($n % 2 == 0)
326  {
327  if ($open)
328  {
329  if ($current == $char)
330  {
331  $open = false;
332  $char = '';
333  }
334  }
335  else
336  {
337  $open = true;
338  $char = $current;
339  }
340  }
341  }
342 
343  if (($current == ';' && !$open) || $i == $end - 1)
344  {
345  $queries[] = substr($sql, $start, ($i - $start + 1));
346  $start = $i + 1;
347  }
348  }
349 
350  return $queries;
351  }
352 
353  /**
354  * Magic method to provide method alias support for quote() and quoteName().
355  *
356  * @param string $method The called method.
357  * @param array $args The array of arguments passed to the method.
358  *
359  * @return mixed The aliased method's return value or null.
360  *
361  * @since 11.1
362  */
363  public function __call($method, $args)
364  {
365  if (empty($args))
366  {
367  return;
368  }
369 
370  switch ($method)
371  {
372  case 'q':
373  return $this->quote($args[0], isset($args[1]) ? $args[1] : true);
374  break;
375  case 'qn':
376  return $this->quoteName($args[0], isset($args[1]) ? $args[1] : null);
377  break;
378  }
379  }
380 
381  /**
382  * Constructor.
383  *
384  * @param array $options List of options used to configure the connection
385  *
386  * @since 11.1
387  */
388  public function __construct($options)
389  {
390  // Initialise object variables.
391  $this->_database = (isset($options['database'])) ? $options['database'] : '';
392 
393  $this->tablePrefix = (isset($options['prefix'])) ? $options['prefix'] : 'jos_';
394  $this->count = 0;
395  $this->errorNum = 0;
396  $this->log = array();
397 
398  // Set class options.
399  $this->options = $options;
400  }
401 
402  /**
403  * Alter database's character set, obtaining query string from protected member.
404  *
405  * @param string $dbName The database name that will be altered
406  *
407  * @return string The query that alter the database query string
408  *
409  * @since 12.2
410  * @throws RuntimeException
411  */
412  public function alterDbCharacterSet($dbName)
413  {
414  if (is_null($dbName))
415  {
416  throw new RuntimeException('Database name must not be null.');
417  }
418 
419  $this->setQuery($this->getAlterDbCharacterSet($dbName));
420 
421  return $this->execute();
422  }
423 
424  /**
425  * Connects to the database if needed.
426  *
427  * @return void Returns void if the database connected successfully.
428  *
429  * @since 12.1
430  * @throws RuntimeException
431  */
432  abstract public function connect();
433 
434  /**
435  * Determines if the connection to the server is active.
436  *
437  * @return boolean True if connected to the database engine.
438  *
439  * @since 11.1
440  */
441  abstract public function connected();
442 
443  /**
444  * Create a new database using information from $options object, obtaining query string
445  * from protected member.
446  *
447  * @param stdClass $options Object used to pass user and database name to database driver.
448  * This object must have "db_name" and "db_user" set.
449  * @param boolean $utf True if the database supports the UTF-8 character set.
450  *
451  * @return string The query that creates database
452  *
453  * @since 12.2
454  * @throws RuntimeException
455  */
456  public function createDatabase($options, $utf = true)
457  {
458  if (is_null($options))
459  {
460  throw new RuntimeException('$options object must not be null.');
461  }
462  elseif (empty($options->db_name))
463  {
464  throw new RuntimeException('$options object must have db_name set.');
465  }
466  elseif (empty($options->db_user))
467  {
468  throw new RuntimeException('$options object must have db_user set.');
469  }
470 
471  $this->setQuery($this->getCreateDatabaseQuery($options, $utf));
472 
473  return $this->execute();
474  }
475 
476  /**
477  * Disconnects the database.
478  *
479  * @return void
480  *
481  * @since 12.1
482  */
483  abstract public function disconnect();
484 
485  /**
486  * Adds a function callable just before disconnecting the database. Parameter of the callable is $this JDatabaseDriver
487  *
488  * @param callable $callable Function to call in disconnect() method just before disconnecting from database
489  *
490  * @return void
491  *
492  * @since CMS 3.1.2
493  */
494  public function addDisconnectHandler($callable)
495  {
496  $this->disconnectHandlers[] = $callable;
497  }
498 
499  /**
500  * Drops a table from the database.
501  *
502  * @param string $table The name of the database table to drop.
503  * @param boolean $ifExists Optionally specify that the table must exist before it is dropped.
504  *
505  * @return JDatabaseDriver Returns this object to support chaining.
506  *
507  * @since 11.4
508  * @throws RuntimeException
509  */
510  public abstract function dropTable($table, $ifExists = true);
511 
512  /**
513  * Escapes a string for usage in an SQL statement.
514  *
515  * @param string $text The string to be escaped.
516  * @param boolean $extra Optional parameter to provide extra escaping.
517  *
518  * @return string The escaped string.
519  *
520  * @since 11.1
521  */
522  abstract public function escape($text, $extra = false);
523 
524  /**
525  * Method to fetch a row from the result set cursor as an array.
526  *
527  * @param mixed $cursor The optional result set cursor from which to fetch the row.
528  *
529  * @return mixed Either the next row from the result set or false if there are no more rows.
530  *
531  * @since 11.1
532  */
533  abstract protected function fetchArray($cursor = null);
534 
535  /**
536  * Method to fetch a row from the result set cursor as an associative array.
537  *
538  * @param mixed $cursor The optional result set cursor from which to fetch the row.
539  *
540  * @return mixed Either the next row from the result set or false if there are no more rows.
541  *
542  * @since 11.1
543  */
544  abstract protected function fetchAssoc($cursor = null);
545 
546  /**
547  * Method to fetch a row from the result set cursor as an object.
548  *
549  * @param mixed $cursor The optional result set cursor from which to fetch the row.
550  * @param string $class The class name to use for the returned row object.
551  *
552  * @return mixed Either the next row from the result set or false if there are no more rows.
553  *
554  * @since 11.1
555  */
556  abstract protected function fetchObject($cursor = null, $class = 'stdClass');
557 
558  /**
559  * Method to free up the memory used for the result set.
560  *
561  * @param mixed $cursor The optional result set cursor from which to fetch the row.
562  *
563  * @return void
564  *
565  * @since 11.1
566  */
567  abstract protected function freeResult($cursor = null);
568 
569  /**
570  * Get the number of affected rows for the previous executed SQL statement.
571  *
572  * @return integer The number of affected rows.
573  *
574  * @since 11.1
575  */
576  abstract public function getAffectedRows();
577 
578  /**
579  * Return the query string to alter the database character set.
580  *
581  * @param string $dbName The database name
582  *
583  * @return string The query that alter the database query string
584  *
585  * @since 12.2
586  */
587  protected function getAlterDbCharacterSet($dbName)
588  {
589  return 'ALTER DATABASE ' . $this->quoteName($dbName) . ' CHARACTER SET `utf8`';
590  }
591 
592  /**
593  * Return the query string to create new Database.
594  * Each database driver, other than MySQL, need to override this member to return correct string.
595  *
596  * @param stdClass $options Object used to pass user and database name to database driver.
597  * This object must have "db_name" and "db_user" set.
598  * @param boolean $utf True if the database supports the UTF-8 character set.
599  *
600  * @return string The query that creates database
601  *
602  * @since 12.2
603  */
604  protected function getCreateDatabaseQuery($options, $utf)
605  {
606  if ($utf)
607  {
608  return 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' CHARACTER SET `utf8`';
609  }
610  return 'CREATE DATABASE ' . $this->quoteName($options->db_name);
611  }
612 
613  /**
614  * Method to get the database collation in use by sampling a text field of a table in the database.
615  *
616  * @return mixed The collation in use by the database or boolean false if not supported.
617  *
618  * @since 11.1
619  */
620  abstract public function getCollation();
621 
622  /**
623  * Method that provides access to the underlying database connection. Useful for when you need to call a
624  * proprietary method such as postgresql's lo_* methods.
625  *
626  * @return resource The underlying database connection resource.
627  *
628  * @since 11.1
629  */
630  public function getConnection()
631  {
632  return $this->connection;
633  }
634 
635  /**
636  * Get the total number of SQL statements executed by the database driver.
637  *
638  * @return integer
639  *
640  * @since 11.1
641  */
642  public function getCount()
643  {
644  return $this->count;
645  }
646 
647  /**
648  * Gets the name of the database used by this conneciton.
649  *
650  * @return string
651  *
652  * @since 11.4
653  */
654  protected function getDatabase()
655  {
656  return $this->_database;
657  }
658 
659  /**
660  * Returns a PHP date() function compliant date format for the database driver.
661  *
662  * @return string The format string.
663  *
664  * @since 11.1
665  */
666  public function getDateFormat()
667  {
668  return 'Y-m-d H:i:s';
669  }
670 
671  /**
672  * Get the database driver SQL statement log.
673  *
674  * @return array SQL statements executed by the database driver.
675  *
676  * @since 11.1
677  */
678  public function getLog()
679  {
680  return $this->log;
681  }
682 
683  /**
684  * Get the database driver SQL statement log.
685  *
686  * @return array SQL statements executed by the database driver.
687  *
688  * @since CMS 3.1.2
689  */
690  public function getTimings()
691  {
692  return $this->timings;
693  }
694 
695  /**
696  * Get the database driver SQL statement log.
697  *
698  * @return array SQL statements executed by the database driver.
699  *
700  * @since CMS 3.1.2
701  */
702  public function getCallStacks()
703  {
704  return $this->callStacks;
705  }
706 
707  /**
708  * Get the minimum supported database version.
709  *
710  * @return string The minimum version number for the database driver.
711  *
712  * @since 12.1
713  */
714  public function getMinimum()
715  {
716  return static::$dbMinimum;
717  }
718 
719  /**
720  * Get the null or zero representation of a timestamp for the database driver.
721  *
722  * @return string Null or zero representation of a timestamp.
723  *
724  * @since 11.1
725  */
726  public function getNullDate()
727  {
728  return $this->nullDate;
729  }
730 
731  /**
732  * Get the number of returned rows for the previous executed SQL statement.
733  *
734  * @param resource $cursor An optional database cursor resource to extract the row count from.
735  *
736  * @return integer The number of returned rows.
737  *
738  * @since 11.1
739  */
740  abstract public function getNumRows($cursor = null);
741 
742  /**
743  * Get the common table prefix for the database driver.
744  *
745  * @return string The common database table prefix.
746  *
747  * @since 11.1
748  */
749  public function getPrefix()
750  {
751  return $this->tablePrefix;
752  }
753 
754  /**
755  * Gets an exporter class object.
756  *
757  * @return JDatabaseExporter An exporter object.
758  *
759  * @since 12.1
760  * @throws RuntimeException
761  */
762  public function getExporter()
763  {
764  // Derive the class name from the driver.
765  $class = 'JDatabaseExporter' . ucfirst($this->name);
766 
767  // Make sure we have an exporter class for this driver.
768  if (!class_exists($class))
769  {
770  // If it doesn't exist we are at an impasse so throw an exception.
771  throw new RuntimeException('Database Exporter not found.');
772  }
773 
774  $o = new $class;
775  $o->setDbo($this);
776 
777  return $o;
778  }
779 
780  /**
781  * Gets an importer class object.
782  *
783  * @return JDatabaseImporter An importer object.
784  *
785  * @since 12.1
786  * @throws RuntimeException
787  */
788  public function getImporter()
789  {
790  // Derive the class name from the driver.
791  $class = 'JDatabaseImporter' . ucfirst($this->name);
792 
793  // Make sure we have an importer class for this driver.
794  if (!class_exists($class))
795  {
796  // If it doesn't exist we are at an impasse so throw an exception.
797  throw new RuntimeException('Database Importer not found');
798  }
799 
800  $o = new $class;
801  $o->setDbo($this);
802 
803  return $o;
804  }
805 
806  /**
807  * Get the current query object or a new JDatabaseQuery object.
808  *
809  * @param boolean $new False to return the current query object, True to return a new JDatabaseQuery object.
810  *
811  * @return JDatabaseQuery The current query object or a new object extending the JDatabaseQuery class.
812  *
813  * @since 11.1
814  * @throws RuntimeException
815  */
816  public function getQuery($new = false)
817  {
818  if ($new)
819  {
820  // Derive the class name from the driver.
821  $class = 'JDatabaseQuery' . ucfirst($this->name);
822 
823  // Make sure we have a query class for this driver.
824  if (!class_exists($class))
825  {
826  // If it doesn't exist we are at an impasse so throw an exception.
827  throw new RuntimeException('Database Query Class not found.');
828  }
829 
830  return new $class($this);
831  }
832  else
833  {
834  return $this->sql;
835  }
836  }
837 
838  /**
839  * Get a new iterator on the current query.
840  *
841  * @param string $column An option column to use as the iterator key.
842  * @param string $class The class of object that is returned.
843  *
844  * @return JDatabaseIterator A new database iterator.
845  *
846  * @since 12.1
847  * @throws RuntimeException
848  */
849  public function getIterator($column = null, $class = 'stdClass')
850  {
851  // Derive the class name from the driver.
852  $iteratorClass = 'JDatabaseIterator' . ucfirst($this->name);
853 
854  // Make sure we have an iterator class for this driver.
855  if (!class_exists($iteratorClass))
856  {
857  // If it doesn't exist we are at an impasse so throw an exception.
858  throw new RuntimeException(sprintf('class *%s* is not defined', $iteratorClass));
859  }
860 
861  // Return a new iterator
862  return new $iteratorClass($this->execute(), $column, $class);
863  }
864 
865  /**
866  * Retrieves field information about the given tables.
867  *
868  * @param string $table The name of the database table.
869  * @param boolean $typeOnly True (default) to only return field types.
870  *
871  * @return array An array of fields by table.
872  *
873  * @since 11.1
874  * @throws RuntimeException
875  */
876  abstract public function getTableColumns($table, $typeOnly = true);
877 
878  /**
879  * Shows the table CREATE statement that creates the given tables.
880  *
881  * @param mixed $tables A table name or a list of table names.
882  *
883  * @return array A list of the create SQL for the tables.
884  *
885  * @since 11.1
886  * @throws RuntimeException
887  */
888  abstract public function getTableCreate($tables);
889 
890  /**
891  * Retrieves field information about the given tables.
892  *
893  * @param mixed $tables A table name or a list of table names.
894  *
895  * @return array An array of keys for the table(s).
896  *
897  * @since 11.1
898  * @throws RuntimeException
899  */
900  abstract public function getTableKeys($tables);
901 
902  /**
903  * Method to get an array of all tables in the database.
904  *
905  * @return array An array of all the tables in the database.
906  *
907  * @since 11.1
908  * @throws RuntimeException
909  */
910  abstract public function getTableList();
911 
912  /**
913  * Determine whether or not the database engine supports UTF-8 character encoding.
914  *
915  * @return boolean True if the database engine supports UTF-8 character encoding.
916  *
917  * @since 11.1
918  * @deprecated 12.3 (Platform) & 4.0 (CMS) - Use hasUTFSupport() instead
919  */
920  public function getUTFSupport()
921  {
922  JLog::add('JDatabaseDriver::getUTFSupport() is deprecated. Use JDatabaseDriver::hasUTFSupport() instead.', JLog::WARNING, 'deprecated');
923  return $this->hasUTFSupport();
924  }
925 
926  /**
927  * Determine whether or not the database engine supports UTF-8 character encoding.
928  *
929  * @return boolean True if the database engine supports UTF-8 character encoding.
930  *
931  * @since 12.1
932  */
933  public function hasUTFSupport()
934  {
935  return $this->utf;
936  }
937 
938  /**
939  * Get the version of the database connector
940  *
941  * @return string The database connector version.
942  *
943  * @since 11.1
944  */
945  abstract public function getVersion();
946 
947  /**
948  * Method to get the auto-incremented value from the last INSERT statement.
949  *
950  * @return mixed The value of the auto-increment field from the last inserted row.
951  *
952  * @since 11.1
953  */
954  abstract public function insertid();
955 
956  /**
957  * Inserts a row into a table based on an object's properties.
958  *
959  * @param string $table The name of the database table to insert into.
960  * @param object &$object A reference to an object whose public properties match the table fields.
961  * @param string $key The name of the primary key. If provided the object property is updated.
962  *
963  * @return boolean True on success.
964  *
965  * @since 11.1
966  * @throws RuntimeException
967  */
968  public function insertObject($table, &$object, $key = null)
969  {
970  $fields = array();
971  $values = array();
972 
973  // Iterate over the object variables to build the query fields and values.
974  foreach (get_object_vars($object) as $k => $v)
975  {
976  // Only process non-null scalars.
977  if (is_array($v) or is_object($v) or $v === null)
978  {
979  continue;
980  }
981 
982  // Ignore any internal fields.
983  if ($k[0] == '_')
984  {
985  continue;
986  }
987 
988  // Prepare and sanitize the fields and values for the database query.
989  $fields[] = $this->quoteName($k);
990  $values[] = $this->quote($v);
991  }
992 
993  // Create the base insert statement.
994  $query = $this->getQuery(true)
995  ->insert($this->quoteName($table))
996  ->columns($fields)
997  ->values(implode(',', $values));
998 
999  // Set the query and execute the insert.
1000  $this->setQuery($query);
1001 
1002  if (!$this->execute())
1003  {
1004  return false;
1005  }
1006 
1007  // Update the primary key if it exists.
1008  $id = $this->insertid();
1009 
1010  if ($key && $id && is_string($key))
1011  {
1012  $object->$key = $id;
1013  }
1014 
1015  return true;
1016  }
1017 
1018  /**
1019  * Method to check whether the installed database version is supported by the database driver
1020  *
1021  * @return boolean True if the database version is supported
1022  *
1023  * @since 12.1
1024  */
1025  public function isMinimumVersion()
1026  {
1027  return version_compare($this->getVersion(), static::$dbMinimum) >= 0;
1028  }
1029 
1030  /**
1031  * Method to get the first row of the result set from the database query as an associative array
1032  * of ['field_name' => 'row_value'].
1033  *
1034  * @return mixed The return value or null if the query failed.
1035  *
1036  * @since 11.1
1037  * @throws RuntimeException
1038  */
1039  public function loadAssoc()
1040  {
1041  $this->connect();
1042 
1043  $ret = null;
1044 
1045  // Execute the query and get the result set cursor.
1046  if (!($cursor = $this->execute()))
1047  {
1048  return null;
1049  }
1050 
1051  // Get the first row from the result set as an associative array.
1052  if ($array = $this->fetchAssoc($cursor))
1053  {
1054  $ret = $array;
1055  }
1056 
1057  // Free up system resources and return.
1058  $this->freeResult($cursor);
1059 
1060  return $ret;
1061  }
1062 
1063  /**
1064  * Method to get an array of the result set rows from the database query where each row is an associative array
1065  * of ['field_name' => 'row_value']. The array of rows can optionally be keyed by a field name, but defaults to
1066  * a sequential numeric array.
1067  *
1068  * NOTE: Chosing to key the result array by a non-unique field name can result in unwanted
1069  * behavior and should be avoided.
1070  *
1071  * @param string $key The name of a field on which to key the result array.
1072  * @param string $column An optional column name. Instead of the whole row, only this column value will be in
1073  * the result array.
1074  *
1075  * @return mixed The return value or null if the query failed.
1076  *
1077  * @since 11.1
1078  * @throws RuntimeException
1079  */
1080  public function loadAssocList($key = null, $column = null)
1081  {
1082  $this->connect();
1083 
1084  $array = array();
1085 
1086  // Execute the query and get the result set cursor.
1087  if (!($cursor = $this->execute()))
1088  {
1089  return null;
1090  }
1091 
1092  // Get all of the rows from the result set.
1093  while ($row = $this->fetchAssoc($cursor))
1094  {
1095  $value = ($column) ? (isset($row[$column]) ? $row[$column] : $row) : $row;
1096 
1097  if ($key)
1098  {
1099  $array[$row[$key]] = $value;
1100  }
1101  else
1102  {
1103  $array[] = $value;
1104  }
1105  }
1106 
1107  // Free up system resources and return.
1108  $this->freeResult($cursor);
1109 
1110  return $array;
1111  }
1112 
1113  /**
1114  * Method to get an array of values from the <var>$offset</var> field in each row of the result set from
1115  * the database query.
1116  *
1117  * @param integer $offset The row offset to use to build the result array.
1118  *
1119  * @return mixed The return value or null if the query failed.
1120  *
1121  * @since 11.1
1122  * @throws RuntimeException
1123  */
1124  public function loadColumn($offset = 0)
1125  {
1126  $this->connect();
1127 
1128  $array = array();
1129 
1130  // Execute the query and get the result set cursor.
1131  if (!($cursor = $this->execute()))
1132  {
1133  return null;
1134  }
1135 
1136  // Get all of the rows from the result set as arrays.
1137  while ($row = $this->fetchArray($cursor))
1138  {
1139  $array[] = $row[$offset];
1140  }
1141 
1142  // Free up system resources and return.
1143  $this->freeResult($cursor);
1144 
1145  return $array;
1146  }
1147 
1148  /**
1149  * Method to get the next row in the result set from the database query as an object.
1150  *
1151  * @param string $class The class name to use for the returned row object.
1152  *
1153  * @return mixed The result of the query as an array, false if there are no more rows.
1154  *
1155  * @since 11.1
1156  * @throws RuntimeException
1157  */
1158  public function loadNextObject($class = 'stdClass')
1159  {
1160  JLog::add(__METHOD__ . '() is deprecated. Use JDatabase::getIterator() instead.', JLog::WARNING, 'deprecated');
1161  $this->connect();
1162 
1163  static $cursor = null;
1164 
1165  // Execute the query and get the result set cursor.
1166  if ( is_null($cursor) )
1167  {
1168  if (!($cursor = $this->execute()))
1169  {
1170  return $this->errorNum ? null : false;
1171  }
1172  }
1173 
1174  // Get the next row from the result set as an object of type $class.
1175  if ($row = $this->fetchObject($cursor, $class))
1176  {
1177  return $row;
1178  }
1179 
1180  // Free up system resources and return.
1181  $this->freeResult($cursor);
1182  $cursor = null;
1183 
1184  return false;
1185  }
1186 
1187  /**
1188  * Method to get the next row in the result set from the database query as an array.
1189  *
1190  * @return mixed The result of the query as an array, false if there are no more rows.
1191  *
1192  * @since 11.1
1193  * @throws RuntimeException
1194  * @deprecated N/A (CMS) Use JDatabaseDriver::getIterator() instead
1195  */
1196  public function loadNextRow()
1197  {
1198  JLog::add('JDatabaseDriver::loadNextRow() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated');
1199  $this->connect();
1200 
1201  static $cursor = null;
1202 
1203  // Execute the query and get the result set cursor.
1204  if ( is_null($cursor) )
1205  {
1206  if (!($cursor = $this->execute()))
1207  {
1208  return $this->errorNum ? null : false;
1209  }
1210  }
1211 
1212  // Get the next row from the result set as an object of type $class.
1213  if ($row = $this->fetchArray($cursor))
1214  {
1215  return $row;
1216  }
1217 
1218  // Free up system resources and return.
1219  $this->freeResult($cursor);
1220  $cursor = null;
1221 
1222  return false;
1223  }
1224 
1225  /**
1226  * Method to get the first row of the result set from the database query as an object.
1227  *
1228  * @param string $class The class name to use for the returned row object.
1229  *
1230  * @return mixed The return value or null if the query failed.
1231  *
1232  * @since 11.1
1233  * @throws RuntimeException
1234  */
1235  public function loadObject($class = 'stdClass')
1236  {
1237  $this->connect();
1238 
1239  $ret = null;
1240 
1241  // Execute the query and get the result set cursor.
1242  if (!($cursor = $this->execute()))
1243  {
1244  return null;
1245  }
1246 
1247  // Get the first row from the result set as an object of type $class.
1248  if ($object = $this->fetchObject($cursor, $class))
1249  {
1250  $ret = $object;
1251  }
1252 
1253  // Free up system resources and return.
1254  $this->freeResult($cursor);
1255 
1256  return $ret;
1257  }
1258 
1259  /**
1260  * Method to get an array of the result set rows from the database query where each row is an object. The array
1261  * of objects can optionally be keyed by a field name, but defaults to a sequential numeric array.
1262  *
1263  * NOTE: Choosing to key the result array by a non-unique field name can result in unwanted
1264  * behavior and should be avoided.
1265  *
1266  * @param string $key The name of a field on which to key the result array.
1267  * @param string $class The class name to use for the returned row objects.
1268  *
1269  * @return mixed The return value or null if the query failed.
1270  *
1271  * @since 11.1
1272  * @throws RuntimeException
1273  */
1274  public function loadObjectList($key = '', $class = 'stdClass')
1275  {
1276  $this->connect();
1277 
1278  $array = array();
1279 
1280  // Execute the query and get the result set cursor.
1281  if (!($cursor = $this->execute()))
1282  {
1283  return null;
1284  }
1285 
1286  // Get all of the rows from the result set as objects of type $class.
1287  while ($row = $this->fetchObject($cursor, $class))
1288  {
1289  if ($key)
1290  {
1291  $array[$row->$key] = $row;
1292  }
1293  else
1294  {
1295  $array[] = $row;
1296  }
1297  }
1298 
1299  // Free up system resources and return.
1300  $this->freeResult($cursor);
1301 
1302  return $array;
1303  }
1304 
1305  /**
1306  * Method to get the first field of the first row of the result set from the database query.
1307  *
1308  * @return mixed The return value or null if the query failed.
1309  *
1310  * @since 11.1
1311  * @throws RuntimeException
1312  */
1313  public function loadResult()
1314  {
1315  $this->connect();
1316 
1317  $ret = null;
1318 
1319  // Execute the query and get the result set cursor.
1320  if (!($cursor = $this->execute()))
1321  {
1322  return null;
1323  }
1324 
1325  // Get the first row from the result set as an array.
1326  if ($row = $this->fetchArray($cursor))
1327  {
1328  $ret = $row[0];
1329  }
1330 
1331  // Free up system resources and return.
1332  $this->freeResult($cursor);
1333 
1334  return $ret;
1335  }
1336 
1337  /**
1338  * Method to get the first row of the result set from the database query as an array. Columns are indexed
1339  * numerically so the first column in the result set would be accessible via <var>$row[0]</var>, etc.
1340  *
1341  * @return mixed The return value or null if the query failed.
1342  *
1343  * @since 11.1
1344  * @throws RuntimeException
1345  */
1346  public function loadRow()
1347  {
1348  $this->connect();
1349 
1350  $ret = null;
1351 
1352  // Execute the query and get the result set cursor.
1353  if (!($cursor = $this->execute()))
1354  {
1355  return null;
1356  }
1357 
1358  // Get the first row from the result set as an array.
1359  if ($row = $this->fetchArray($cursor))
1360  {
1361  $ret = $row;
1362  }
1363 
1364  // Free up system resources and return.
1365  $this->freeResult($cursor);
1366 
1367  return $ret;
1368  }
1369 
1370  /**
1371  * Method to get an array of the result set rows from the database query where each row is an array. The array
1372  * of objects can optionally be keyed by a field offset, but defaults to a sequential numeric array.
1373  *
1374  * NOTE: Choosing to key the result array by a non-unique field can result in unwanted
1375  * behavior and should be avoided.
1376  *
1377  * @param string $key The name of a field on which to key the result array.
1378  *
1379  * @return mixed The return value or null if the query failed.
1380  *
1381  * @since 11.1
1382  * @throws RuntimeException
1383  */
1384  public function loadRowList($key = null)
1385  {
1386  $this->connect();
1387 
1388  $array = array();
1389 
1390  // Execute the query and get the result set cursor.
1391  if (!($cursor = $this->execute()))
1392  {
1393  return null;
1394  }
1395 
1396  // Get all of the rows from the result set as arrays.
1397  while ($row = $this->fetchArray($cursor))
1398  {
1399  if ($key !== null)
1400  {
1401  $array[$row[$key]] = $row;
1402  }
1403  else
1404  {
1405  $array[] = $row;
1406  }
1407  }
1408 
1409  // Free up system resources and return.
1410  $this->freeResult($cursor);
1411 
1412  return $array;
1413  }
1414 
1415  /**
1416  * Locks a table in the database.
1417  *
1418  * @param string $tableName The name of the table to unlock.
1419  *
1420  * @return JDatabaseDriver Returns this object to support chaining.
1421  *
1422  * @since 11.4
1423  * @throws RuntimeException
1424  */
1425  public abstract function lockTable($tableName);
1426 
1427  /**
1428  * Quotes and optionally escapes a string to database requirements for use in database queries.
1429  *
1430  * @param mixed $text A string or an array of strings to quote.
1431  * @param boolean $escape True (default) to escape the string, false to leave it unchanged.
1432  *
1433  * @return string The quoted input string.
1434  *
1435  * @note Accepting an array of strings was added in 12.3.
1436  * @since 11.1
1437  */
1438  public function quote($text, $escape = true)
1439  {
1440  if (is_array($text))
1441  {
1442  foreach ($text as $k => $v)
1443  {
1444  $text[$k] = $this->quote($v, $escape);
1445  }
1446 
1447  return $text;
1448  }
1449  else
1450  {
1451  return '\'' . ($escape ? $this->escape($text) : $text) . '\'';
1452  }
1453  }
1454 
1455  /**
1456  * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection
1457  * risks and reserved word conflicts.
1458  *
1459  * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
1460  * Each type supports dot-notation name.
1461  * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be
1462  * same length of $name; if is null there will not be any AS part for string or array element.
1463  *
1464  * @return mixed The quote wrapped name, same type of $name.
1465  *
1466  * @since 11.1
1467  */
1468  public function quoteName($name, $as = null)
1469  {
1470  if (is_string($name))
1471  {
1472  $quotedName = $this->quoteNameStr(explode('.', $name));
1473 
1474  $quotedAs = '';
1475 
1476  if (!is_null($as))
1477  {
1478  settype($as, 'array');
1479  $quotedAs .= ' AS ' . $this->quoteNameStr($as);
1480  }
1481 
1482  return $quotedName . $quotedAs;
1483  }
1484  else
1485  {
1486  $fin = array();
1487 
1488  if (is_null($as))
1489  {
1490  foreach ($name as $str)
1491  {
1492  $fin[] = $this->quoteName($str);
1493  }
1494  }
1495  elseif (is_array($name) && (count($name) == count($as)))
1496  {
1497  $count = count($name);
1498 
1499  for ($i = 0; $i < $count; $i++)
1500  {
1501  $fin[] = $this->quoteName($name[$i], $as[$i]);
1502  }
1503  }
1504 
1505  return $fin;
1506  }
1507  }
1508 
1509  /**
1510  * Quote strings coming from quoteName call.
1511  *
1512  * @param array $strArr Array of strings coming from quoteName dot-explosion.
1513  *
1514  * @return string Dot-imploded string of quoted parts.
1515  *
1516  * @since 11.3
1517  */
1518  protected function quoteNameStr($strArr)
1519  {
1520  $parts = array();
1521  $q = $this->nameQuote;
1522 
1523  foreach ($strArr as $part)
1524  {
1525  if (is_null($part))
1526  {
1527  continue;
1528  }
1529 
1530  if (strlen($q) == 1)
1531  {
1532  $parts[] = $q . $part . $q;
1533  }
1534  else
1535  {
1536  $parts[] = $q{0} . $part . $q{1};
1537  }
1538  }
1539 
1540  return implode('.', $parts);
1541  }
1542 
1543  /**
1544  * This function replaces a string identifier <var>$prefix</var> with the string held is the
1545  * <var>tablePrefix</var> class variable.
1546  *
1547  * @param string $sql The SQL statement to prepare.
1548  * @param string $prefix The common table prefix.
1549  *
1550  * @return string The processed SQL statement.
1551  *
1552  * @since 11.1
1553  */
1554  public function replacePrefix($sql, $prefix = '#__')
1555  {
1556  /*
1557  * Pattern is: find any non-quoted (which is not including single or double quotes) string being the prefix
1558  * in $sql possibly followed by a double or single quoted one:
1559  * (
1560  * not including quotes:
1561  * positive lookahead: (?=
1562  * not including " or ': [^"\']+
1563  * )
1564  * including exactly the prefix to replace: preg_quote( $prefix, '/' )
1565  * )(
1566  * Followed by a double-quoted: "(?:[^\\"]|\\.)*"
1567  * Or: |
1568  * single-quoted: \'(?:[^\\\']|\\.)*\'
1569  * )
1570  * possibly: ?
1571  * $pattern = '/((?=[^"\']+)' . preg_quote($prefix, '/') . ')("(?:[^\\"]|\\.)*"|\'(?:[^\\\']|\\.)*\')?/';
1572  */
1573  $pattern = '/(?<=[^"\'])(' . preg_quote($prefix, '/') . ')("(?:[^\\\\"]|\.)*"|\'(?:[^\\\\\']|\.)*\')?/';
1574 
1575  return preg_replace($pattern, $this->getPrefix() . '\\2', $sql);
1576  }
1577 
1578  /**
1579  * Renames a table in the database.
1580  *
1581  * @param string $oldTable The name of the table to be renamed
1582  * @param string $newTable The new name for the table.
1583  * @param string $backup Table prefix
1584  * @param string $prefix For the table - used to rename constraints in non-mysql databases
1585  *
1586  * @return JDatabaseDriver Returns this object to support chaining.
1587  *
1588  * @since 11.4
1589  * @throws RuntimeException
1590  */
1591  public abstract function renameTable($oldTable, $newTable, $backup = null, $prefix = null);
1592 
1593  /**
1594  * Select a database for use.
1595  *
1596  * @param string $database The name of the database to select for use.
1597  *
1598  * @return boolean True if the database was successfully selected.
1599  *
1600  * @since 11.1
1601  * @throws RuntimeException
1602  */
1603  abstract public function select($database);
1604 
1605  /**
1606  * Sets the database debugging state for the driver.
1607  *
1608  * @param boolean $level True to enable debugging.
1609  *
1610  * @return boolean The old debugging level.
1611  *
1612  * @since 11.1
1613  */
1614  public function setDebug($level)
1615  {
1616  $previous = $this->debug;
1617  $this->debug = (bool) $level;
1618 
1619  return $previous;
1620  }
1621 
1622  /**
1623  * Sets the SQL statement string for later execution.
1624  *
1625  * @param mixed $query The SQL statement to set either as a JDatabaseQuery object or a string.
1626  * @param integer $offset The affected row offset to set.
1627  * @param integer $limit The maximum affected rows to set.
1628  *
1629  * @return JDatabaseDriver This object to support method chaining.
1630  *
1631  * @since 11.1
1632  */
1633  public function setQuery($query, $offset = 0, $limit = 0)
1634  {
1635  $this->sql = $query;
1636 
1637  if ($query instanceof JDatabaseQueryLimitable)
1638  {
1639  $query->setLimit($limit, $offset);
1640  }
1641  else
1642  {
1643  $this->limit = (int) max(0, $limit);
1644  $this->offset = (int) max(0, $offset);
1645  }
1646 
1647  return $this;
1648  }
1649 
1650  /**
1651  * Set the connection to use UTF-8 character encoding.
1652  *
1653  * @return boolean True on success.
1654  *
1655  * @since 11.1
1656  */
1657  abstract public function setUTF();
1658 
1659  /**
1660  * Method to commit a transaction.
1661  *
1662  * @param boolean $toSavepoint If true, commit to the last savepoint.
1663  *
1664  * @return void
1665  *
1666  * @since 11.1
1667  * @throws RuntimeException
1668  */
1669  abstract public function transactionCommit($toSavepoint = false);
1670 
1671  /**
1672  * Method to roll back a transaction.
1673  *
1674  * @param boolean $toSavepoint If true, rollback to the last savepoint.
1675  *
1676  * @return void
1677  *
1678  * @since 11.1
1679  * @throws RuntimeException
1680  */
1681  abstract public function transactionRollback($toSavepoint = false);
1682 
1683  /**
1684  * Method to initialize a transaction.
1685  *
1686  * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created.
1687  *
1688  * @return void
1689  *
1690  * @since 11.1
1691  * @throws RuntimeException
1692  */
1693  abstract public function transactionStart($asSavepoint = false);
1694 
1695  /**
1696  * Method to truncate a table.
1697  *
1698  * @param string $table The table to truncate
1699  *
1700  * @return void
1701  *
1702  * @since 11.3
1703  * @throws RuntimeException
1704  */
1705  public function truncateTable($table)
1706  {
1707  $this->setQuery('TRUNCATE TABLE ' . $this->quoteName($table));
1708  $this->execute();
1709  }
1710 
1711  /**
1712  * Updates a row in a table based on an object's properties.
1713  *
1714  * @param string $table The name of the database table to update.
1715  * @param object &$object A reference to an object whose public properties match the table fields.
1716  * @param array $key The name of the primary key.
1717  * @param boolean $nulls True to update null fields or false to ignore them.
1718  *
1719  * @return boolean True on success.
1720  *
1721  * @since 11.1
1722  * @throws RuntimeException
1723  */
1724  public function updateObject($table, &$object, $key, $nulls = false)
1725  {
1726  $fields = array();
1727  $where = array();
1728 
1729  if (is_string($key))
1730  {
1731  $key = array($key);
1732  }
1733 
1734  if (is_object($key))
1735  {
1736  $key = (array) $key;
1737  }
1738 
1739  // Create the base update statement.
1740  $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s';
1741 
1742  // Iterate over the object variables to build the query fields/value pairs.
1743  foreach (get_object_vars($object) as $k => $v)
1744  {
1745  // Only process scalars that are not internal fields.
1746  if (is_array($v) or is_object($v) or $k[0] == '_')
1747  {
1748  continue;
1749  }
1750 
1751  // Set the primary key to the WHERE clause instead of a field to update.
1752  if (in_array($k, $key))
1753  {
1754  $where[] = $this->quoteName($k) . '=' . $this->quote($v);
1755  continue;
1756  }
1757 
1758  // Prepare and sanitize the fields and values for the database query.
1759  if ($v === null)
1760  {
1761  // If the value is null and we want to update nulls then set it.
1762  if ($nulls)
1763  {
1764  $val = 'NULL';
1765  }
1766  // If the value is null and we do not want to update nulls then ignore this field.
1767  else
1768  {
1769  continue;
1770  }
1771  }
1772  // The field is not null so we prep it for update.
1773  else
1774  {
1775  $val = $this->quote($v);
1776  }
1777 
1778  // Add the field to be updated.
1779  $fields[] = $this->quoteName($k) . '=' . $val;
1780  }
1781 
1782  // We don't have any fields to update.
1783  if (empty($fields))
1784  {
1785  return true;
1786  }
1787 
1788  // Set the query and execute the update.
1789  $this->setQuery(sprintf($statement, implode(",", $fields), implode(' AND ', $where)));
1790 
1791  return $this->execute();
1792  }
1793 
1794  /**
1795  * Execute the SQL statement.
1796  *
1797  * @return mixed A database cursor resource on success, boolean false on failure.
1798  *
1799  * @since 12.1
1800  * @throws RuntimeException
1801  */
1802  abstract public function execute();
1803 
1804  /**
1805  * Unlocks tables in the database.
1806  *
1807  * @return JDatabaseDriver Returns this object to support chaining.
1808  *
1809  * @since 11.4
1810  * @throws RuntimeException
1811  */
1812  public abstract function unlockTables();
1813 }