PageRenderTime 57ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/joomla/database/database.php

https://github.com/joebushi/joomla
PHP | 812 lines | 560 code | 60 blank | 192 comment | 26 complexity | db84406c03465831fd804e6571e15ac9 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0
  1. <?php
  2. /**
  3. * @version $Id$
  4. * @copyright Copyright (C) 2005 - 2010 Open Source Matters, Inc. All rights reserved.
  5. * @license GNU General Public License version 2 or later; see LICENSE.txt
  6. */
  7. // No direct access
  8. defined('JPATH_BASE') or die;
  9. /**
  10. * Database connector class
  11. *
  12. * @package Joomla.Framework
  13. * @subpackage Database
  14. * @since 1.0
  15. */
  16. abstract class JDatabase extends JObject
  17. {
  18. /**
  19. * The database driver name
  20. *
  21. * @var string
  22. */
  23. public $name = '';
  24. /**
  25. * The query sql string
  26. *
  27. * @var string
  28. **/
  29. protected $_sql = '';
  30. /**
  31. * The database error number
  32. *
  33. * @var int
  34. **/
  35. protected $_errorNum = 0;
  36. /**
  37. * The database error message
  38. *
  39. * @var string
  40. */
  41. protected $_errorMsg = '';
  42. /**
  43. * The prefix used on all database tables
  44. *
  45. * @var string
  46. */
  47. protected $_table_prefix = '';
  48. /**
  49. * The database link identifier.
  50. *
  51. * @var mixed
  52. */
  53. protected $_connection = '';
  54. /**
  55. * The last query cursor
  56. *
  57. * @var resource
  58. */
  59. protected $_cursor = null;
  60. /**
  61. * Debug option
  62. *
  63. * @var boolean
  64. */
  65. protected $_debug = 0;
  66. /**
  67. * The limit for the query
  68. *
  69. * @var int
  70. */
  71. protected $_limit = 0;
  72. /**
  73. * The for offset for the limit
  74. *
  75. * @var int
  76. */
  77. protected $_offset = 0;
  78. /**
  79. * The number of queries performed by the object instance
  80. *
  81. * @var int
  82. */
  83. protected $_ticker = 0;
  84. /**
  85. * A log of queries
  86. *
  87. * @var array
  88. */
  89. protected $_log = null;
  90. /**
  91. * The null/zero date string
  92. *
  93. * @var string
  94. */
  95. protected $_nullDate = null;
  96. /**
  97. * Quote for named objects
  98. *
  99. * @var string
  100. */
  101. protected $_nameQuote = null;
  102. /**
  103. * UTF-8 support
  104. *
  105. * @var boolean
  106. * @since 1.5
  107. */
  108. protected $_utf = 0;
  109. /**
  110. * The fields that are to be quote
  111. *
  112. * @var array
  113. * @since 1.5
  114. */
  115. protected $_quoted = null;
  116. /**
  117. * Legacy compatibility
  118. *
  119. * @var bool
  120. * @since 1.5
  121. */
  122. protected $_hasQuoted = null;
  123. /**
  124. * Database object constructor
  125. *
  126. * @param array List of options used to configure the connection
  127. * @since 1.5
  128. */
  129. public function __construct($options)
  130. {
  131. $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : 'jos_';
  132. // Determine utf-8 support.
  133. $this->_utf = $this->hasUTF();
  134. // Set charactersets (needed for MySQL 4.1.2+).
  135. if ($this->_utf){
  136. $this->setUTF();
  137. }
  138. $this->_table_prefix = $prefix;
  139. $this->_ticker = 0;
  140. $this->_errorNum = 0;
  141. $this->_log = array();
  142. $this->_quoted = array();
  143. $this->_hasQuoted = false;
  144. }
  145. /**
  146. * Returns the global Database object, only creating it
  147. * if it doesn't already exist.
  148. *
  149. * The 'driver' entry in the parameters array specifies the database driver
  150. * to be used (defaults to 'mysql' if omitted). All other parameters are
  151. * database driver dependent.
  152. *
  153. * @param array Parameters to be passed to the database driver
  154. * @return JDatabase A database object
  155. * @since 1.5
  156. */
  157. public static function getInstance($options = array())
  158. {
  159. static $instances;
  160. if (!isset($instances)) {
  161. $instances = array();
  162. }
  163. $signature = serialize($options);
  164. if (empty($instances[$signature])) {
  165. $driver = array_key_exists('driver', $options) ? $options['driver'] : 'mysql';
  166. $select = array_key_exists('select', $options) ? $options['select'] : true;
  167. $database = array_key_exists('database', $options) ? $options['database'] : null;
  168. $driver = preg_replace('/[^A-Z0-9_\.-]/i', '', $driver);
  169. $path = dirname(__FILE__).DS.'database'.DS.$driver.'.php';
  170. if (file_exists($path)) {
  171. require_once $path;
  172. } else {
  173. JError::setErrorHandling(E_ERROR, 'die'); //force error type to die
  174. return JError::raiseError(500, JTEXT::_('UNABLE_TO_LOAD_DATABASE_DRIVER:') .$driver);
  175. }
  176. $adapter = 'JDatabase'.$driver;
  177. $instance = new $adapter($options);
  178. if ($error = $instance->getErrorMsg()) {
  179. JError::setErrorHandling(E_ERROR, 'ignore'); //force error type to die
  180. return JError::raiseError(500, JTEXT::_('UNABLE_TO_CONNECT_TO_THE_DATABASE') .$error);
  181. }
  182. $instances[$signature] = & $instance;
  183. }
  184. return $instances[$signature];
  185. }
  186. /**
  187. * Database object destructor
  188. *
  189. * @return boolean
  190. * @since 1.5
  191. */
  192. public function __destruct()
  193. {
  194. return true;
  195. }
  196. /**
  197. * Get the database connectors
  198. *
  199. * @return array An array of available session handlers
  200. */
  201. public function getConnectors()
  202. {
  203. jimport('joomla.filesystem.folder');
  204. $handlers = JFolder::files(dirname(__FILE__).DS.'database', '.php$');
  205. $names = array();
  206. foreach($handlers as $handler) {
  207. $name = substr($handler, 0, strrpos($handler, '.'));
  208. $class = 'JDatabase'.ucfirst($name);
  209. if (!class_exists($class)) {
  210. require_once dirname(__FILE__).DS.'database'.DS.$name.'.php';
  211. }
  212. if (call_user_func_array(array(trim($class), 'test'), array())) {
  213. $names[] = $name;
  214. }
  215. }
  216. return $names;
  217. }
  218. /**
  219. * Test to see if the MySQLi connector is available
  220. *
  221. * @return boolean True on success, false otherwise.
  222. */
  223. abstract public function test();
  224. /**
  225. * Determines if the connection to the server is active.
  226. *
  227. * @return boolean
  228. * @since 1.5
  229. */
  230. abstract public function connected();
  231. /**
  232. * Determines UTF support
  233. *
  234. * @return boolean
  235. * @since 1.5
  236. */
  237. abstract public function hasUTF();
  238. /**
  239. * Custom settings for UTF support
  240. *
  241. * @since 1.5
  242. */
  243. abstract public function setUTF();
  244. /**
  245. * Adds a field or array of field names to the list that are to be quoted
  246. *
  247. * @param mixed Field name or array of names
  248. * @since 1.5
  249. */
  250. public function addQuoted($quoted)
  251. {
  252. if (is_string($quoted)) {
  253. $this->_quoted[] = $quoted;
  254. } else {
  255. $this->_quoted = array_merge($this->_quoted, (array)$quoted);
  256. }
  257. $this->_hasQuoted = true;
  258. }
  259. /**
  260. * Splits a string of queries into an array of individual queries
  261. *
  262. * @param string The queries to split
  263. * @return array queries
  264. */
  265. public function splitSql($queries)
  266. {
  267. $start = 0;
  268. $open = false;
  269. $open_char = '';
  270. $end = strlen($queries);
  271. $query_split = array();
  272. for ($i = 0; $i < $end; $i++) {
  273. $current = substr($queries,$i,1);
  274. if (($current == '"' || $current == '\'')) {
  275. $n = 2;
  276. while(substr($queries,$i - $n + 1, 1) == '\\' && $n < $i) {
  277. $n ++;
  278. }
  279. if ($n%2==0) {
  280. if ($open) {
  281. if ($current == $open_char) {
  282. $open = false;
  283. $open_char = '';
  284. }
  285. } else {
  286. $open = true;
  287. $open_char = $current;
  288. }
  289. }
  290. }
  291. if (($current == ';' && !$open)|| $i == $end - 1) {
  292. $query_split[] = substr($queries, $start, ($i - $start + 1));
  293. $start = $i + 1;
  294. }
  295. }
  296. return $query_split;
  297. }
  298. /**
  299. * Checks if field name needs to be quoted
  300. *
  301. * @param string The field name
  302. * @return bool
  303. */
  304. public function isQuoted($fieldName)
  305. {
  306. if ($this->_hasQuoted) {
  307. return in_array($fieldName, $this->_quoted);
  308. } else {
  309. return true;
  310. }
  311. }
  312. /**
  313. * Sets the debug level on or off
  314. *
  315. * @param int 0 = off, 1 = on
  316. */
  317. public function debug($level)
  318. {
  319. $this->_debug = intval($level);
  320. }
  321. /**
  322. * Get the database UTF-8 support
  323. *
  324. * @return boolean
  325. * @since 1.5
  326. */
  327. public function getUTFSupport()
  328. {
  329. return $this->_utf;
  330. }
  331. /**
  332. * Get the error number
  333. *
  334. * @return int The error number for the most recent query
  335. */
  336. public function getErrorNum()
  337. {
  338. return $this->_errorNum;
  339. }
  340. /**
  341. * Get the error message
  342. *
  343. * @return string The error message for the most recent query
  344. */
  345. public function getErrorMsg($escaped = false)
  346. {
  347. if ($escaped) {
  348. return addslashes($this->_errorMsg);
  349. } else {
  350. return $this->_errorMsg;
  351. }
  352. }
  353. /**
  354. * Get a database escaped string
  355. *
  356. * @param string The string to be escaped
  357. * @param boolean Optional parameter to provide extra escaping
  358. * @return string
  359. */
  360. abstract public function getEscaped($text, $extra = false);
  361. /**
  362. * Get a database error log
  363. *
  364. * @return array
  365. */
  366. public function getLog()
  367. {
  368. return $this->_log;
  369. }
  370. /**
  371. * Get the total number of queries made
  372. *
  373. * @return array
  374. */
  375. public function getTicker()
  376. {
  377. return $this->_ticker;
  378. }
  379. /**
  380. * Quote an identifier name (field, table, etc)
  381. *
  382. * @param string The name
  383. * @return string The quoted name
  384. */
  385. public function nameQuote($s)
  386. {
  387. // Only quote if the name is not using dot-notation
  388. if (strpos($s, '.') === false) {
  389. $q = $this->_nameQuote;
  390. if (strlen($q) == 1) {
  391. return $q . $s . $q;
  392. } else {
  393. return $q{0} . $s . $q{1};
  394. }
  395. } else {
  396. return $s;
  397. }
  398. }
  399. /**
  400. * Get the database table prefix
  401. *
  402. * @return string The database prefix
  403. */
  404. public function getPrefix()
  405. {
  406. return $this->_table_prefix;
  407. }
  408. /**
  409. * Get the connection
  410. *
  411. * Provides access to the underlying database connection. Useful for when
  412. * you need to call a proprietary method such as postgresql's lo_* methods
  413. *
  414. * @return resource
  415. */
  416. public function getConnection()
  417. {
  418. return $this->_connection;
  419. }
  420. /**
  421. * Get the database null date
  422. *
  423. * @return string Quoted null/zero date string
  424. */
  425. public function getNullDate()
  426. {
  427. return $this->_nullDate;
  428. }
  429. /**
  430. * Sets the SQL query string for later execution.
  431. *
  432. * This function replaces a string identifier <var>$prefix</var> with the
  433. * string held is the <var>_table_prefix</var> class variable.
  434. *
  435. * @param string The SQL query
  436. * @param string The offset to start selection
  437. * @param string The number of results to return
  438. * @param string The common table prefix
  439. *
  440. * @return object This object to support chaining.
  441. */
  442. public function setQuery($sql, $offset = 0, $limit = 0, $prefix='#__')
  443. {
  444. $this->_sql = $this->replacePrefix((string)$sql, $prefix);
  445. $this->_limit = (int) $limit;
  446. $this->_offset = (int) $offset;
  447. return $this;
  448. }
  449. /**
  450. * This function replaces a string identifier <var>$prefix</var> with the
  451. * string held is the <var>_table_prefix</var> class variable.
  452. *
  453. * @param string The SQL query
  454. * @param string The common table prefix
  455. */
  456. public function replacePrefix($sql, $prefix='#__')
  457. {
  458. $sql = trim($sql);
  459. $escaped = false;
  460. $quoteChar = '';
  461. $n = strlen($sql);
  462. $startPos = 0;
  463. $literal = '';
  464. while ($startPos < $n) {
  465. $ip = strpos($sql, $prefix, $startPos);
  466. if ($ip === false) {
  467. break;
  468. }
  469. $j = strpos($sql, "'", $startPos);
  470. $k = strpos($sql, '"', $startPos);
  471. if (($k !== FALSE) && (($k < $j) || ($j === FALSE))) {
  472. $quoteChar = '"';
  473. $j = $k;
  474. } else {
  475. $quoteChar = "'";
  476. }
  477. if ($j === false) {
  478. $j = $n;
  479. }
  480. $literal .= str_replace($prefix, $this->_table_prefix,substr($sql, $startPos, $j - $startPos));
  481. $startPos = $j;
  482. $j = $startPos + 1;
  483. if ($j >= $n) {
  484. break;
  485. }
  486. // quote comes first, find end of quote
  487. while (TRUE) {
  488. $k = strpos($sql, $quoteChar, $j);
  489. $escaped = false;
  490. if ($k === false) {
  491. break;
  492. }
  493. $l = $k - 1;
  494. while ($l >= 0 && $sql{$l} == '\\') {
  495. $l--;
  496. $escaped = !$escaped;
  497. }
  498. if ($escaped) {
  499. $j = $k+1;
  500. continue;
  501. }
  502. break;
  503. }
  504. if ($k === FALSE) {
  505. // error in the query - no end quote; ignore it
  506. break;
  507. }
  508. $literal .= substr($sql, $startPos, $k - $startPos + 1);
  509. $startPos = $k+1;
  510. }
  511. if ($startPos < $n) {
  512. $literal .= substr($sql, $startPos, $n - $startPos);
  513. }
  514. return $literal;
  515. }
  516. /**
  517. * Get the active query
  518. *
  519. * @return string The current value of the internal SQL vairable
  520. */
  521. public function getQuery()
  522. {
  523. return $this->_sql;
  524. }
  525. /**
  526. * Execute the query
  527. *
  528. * @return mixed A database resource if successful, FALSE if not.
  529. */
  530. abstract public function query();
  531. /**
  532. * Get the affected rows by the most recent query
  533. *
  534. * @return int The number of affected rows in the previous operation
  535. * @since 1.0.5
  536. */
  537. abstract public function getAffectedRows();
  538. /**
  539. * Execute a batch query
  540. *
  541. * @return mixed A database resource if successful, FALSE if not.
  542. */
  543. abstract public function queryBatch($abort_on_error=true, $p_transaction_safe = false);
  544. /**
  545. * Diagnostic function
  546. */
  547. abstract public function explain();
  548. /**
  549. * Get the number of rows returned by the most recent query
  550. *
  551. * @param object Database resource
  552. * @return int The number of rows
  553. */
  554. abstract public function getNumRows($cur=null);
  555. /**
  556. * This method loads the first field of the first row returned by the query.
  557. *
  558. * @return mixed The value returned in the query or null if the query failed.
  559. */
  560. abstract public function loadResult();
  561. /**
  562. * Load an array of single field results into an array
  563. */
  564. abstract public function loadResultArray($numinarray = 0);
  565. /**
  566. * Fetch a result row as an associative array
  567. */
  568. abstract public function loadAssoc();
  569. /**
  570. * Load a associactive list of database rows
  571. *
  572. * @param string The field name of a primary key
  573. * @param string An optional column name. Instead of the whole row, only this column value will be in the return array.
  574. * @return array If key is empty as sequential list of returned records.
  575. */
  576. abstract public function loadAssocList($key = null, $column = null);
  577. /**
  578. * This global function loads the first row of a query into an object
  579. *
  580. * @return object
  581. */
  582. abstract public function loadObject();
  583. /**
  584. * Load a list of database objects
  585. *
  586. * @param string The field name of a primary key
  587. * @return array If <var>key</var> is empty as sequential list of returned records.
  588. * If <var>key</var> is not empty then the returned array is indexed by the value
  589. * the database key. Returns <var>null</var> if the query fails.
  590. */
  591. abstract public function loadObjectList($key='');
  592. /**
  593. * Load the first row returned by the query
  594. *
  595. * @return mixed The first row of the query.
  596. */
  597. abstract public function loadRow();
  598. /**
  599. * Load a list of database rows (numeric column indexing)
  600. *
  601. * If <var>key</var> is not empty then the returned array is indexed by the value
  602. * the database key. Returns <var>null</var> if the query fails.
  603. *
  604. * @param string The field name of a primary key
  605. * @return array
  606. */
  607. abstract public function loadRowList($key='');
  608. /**
  609. * Load the next row returned by the query.
  610. *
  611. * @return mixed The result of the query as an array, false if there are no more rows, or null on an error.
  612. *
  613. * @since 1.6.0
  614. */
  615. abstract public function loadNextRow();
  616. /**
  617. * Load the next row returned by the query.
  618. *
  619. * @return mixed The result of the query as an object, false if there are no more rows, or null on an error.
  620. *
  621. * @since 1.6.0
  622. */
  623. abstract public function loadNextObject();
  624. /**
  625. * Inserts a row into a table based on an objects properties
  626. * @param string The name of the table
  627. * @param object An object whose properties match table fields
  628. * @param string The name of the primary key. If provided the object property is updated.
  629. */
  630. abstract public function insertObject($table, &$object, $keyName = NULL);
  631. /**
  632. * Update an object in the database
  633. *
  634. * @param string
  635. * @param object
  636. * @param string
  637. * @param boolean
  638. */
  639. abstract public function updateObject($table, &$object, $keyName, $updateNulls=true);
  640. /**
  641. * Print out an error statement
  642. *
  643. * @param boolean If TRUE, displays the last SQL statement sent to the database
  644. * @return string A standised error message
  645. * @deprecated 1.6.0 - Jul 2, 2009
  646. */
  647. public function stderr($showSQL = false)
  648. {
  649. if ($this->_errorNum != 0) {
  650. return "DB function failed with error number $this->_errorNum"
  651. ."<br /><font color=\"red\">$this->_errorMsg</font>"
  652. .($showSQL ? "<br />SQL = <pre>$this->_sql</pre>" : '');
  653. } else {
  654. return "DB function reports no errors";
  655. }
  656. }
  657. /**
  658. * Get the ID generated from the previous INSERT operation
  659. *
  660. * @return mixed
  661. */
  662. abstract public function insertid();
  663. /**
  664. * Get the database collation
  665. *
  666. * @return string Collation in use
  667. */
  668. abstract public function getCollation();
  669. /**
  670. * Get the version of the database connector
  671. */
  672. public function getVersion()
  673. {
  674. return 'Not available for this connector';
  675. }
  676. /**
  677. * List tables in a database
  678. *
  679. * @return array A list of all the tables in the database
  680. */
  681. abstract public function getTableList();
  682. /**
  683. * Shows the CREATE TABLE statement that creates the given tables
  684. *
  685. * @param array|string A table name or a list of table names
  686. * @return array A list the create SQL for the tables
  687. */
  688. abstract public function getTableCreate($tables);
  689. /**
  690. * Retrieves information about the given tables
  691. *
  692. * @param array|string A table name or a list of table names
  693. * @param boolean Only return field types, default true
  694. * @return array An array of fields by table
  695. */
  696. abstract public function getTableFields($tables, $typeonly = true);
  697. /**
  698. * Get a quoted database escaped string
  699. *
  700. * @param string A string
  701. * @param boolean Default true to escape string, false to leave the string unchanged
  702. * @return string
  703. */
  704. public function quote($text, $escaped = true)
  705. {
  706. return '\''.($escaped ? $this->getEscaped($text) : $text).'\'';
  707. }
  708. }