PageRenderTime 88ms CodeModel.GetById 45ms RepoModel.GetById 1ms app.codeStats 0ms

/Query.php

https://github.com/Rahisi/database
PHP | 1641 lines | 691 code | 186 blank | 764 comment | 53 complexity | e22969795e6bc8f3ed8bc9046399bc52 MD5 | raw file
  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Database
  5. *
  6. * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. namespace Joomla\Database;
  10. defined('JPATH_PLATFORM') or die;
  11. use RuntimeException;
  12. /**
  13. * Query Building Class.
  14. *
  15. * @package Joomla.Platform
  16. * @subpackage Database
  17. * @since 11.1
  18. *
  19. * @method string q() q($text, $escape = true) Alias for quote method
  20. * @method string qn() qs($name, $as = null) Alias for quoteName method
  21. * @method string e() e($text, $extra = false) Alias for escape method
  22. */
  23. abstract class Query
  24. {
  25. /**
  26. * @var Driver The database driver.
  27. * @since 11.1
  28. */
  29. protected $db = null;
  30. /**
  31. * @var string The SQL query (if a direct query string was provided).
  32. * @since 12.1
  33. */
  34. protected $sql = null;
  35. /**
  36. * @var string The query type.
  37. * @since 11.1
  38. */
  39. protected $type = '';
  40. /**
  41. * @var \Joomla\Database\Query\Element The query element for a generic query (type = null).
  42. * @since 11.1
  43. */
  44. protected $element = null;
  45. /**
  46. * @var \Joomla\Database\Query\Element The select element.
  47. * @since 11.1
  48. */
  49. protected $select = null;
  50. /**
  51. * @var \Joomla\Database\Query\Element The delete element.
  52. * @since 11.1
  53. */
  54. protected $delete = null;
  55. /**
  56. * @var \Joomla\Database\Query\Element The update element.
  57. * @since 11.1
  58. */
  59. protected $update = null;
  60. /**
  61. * @var \Joomla\Database\Query\Element The insert element.
  62. * @since 11.1
  63. */
  64. protected $insert = null;
  65. /**
  66. * @var \Joomla\Database\Query\Element The from element.
  67. * @since 11.1
  68. */
  69. protected $from = null;
  70. /**
  71. * @var \Joomla\Database\Query\Element The join element.
  72. * @since 11.1
  73. */
  74. protected $join = null;
  75. /**
  76. * @var \Joomla\Database\Query\Element The set element.
  77. * @since 11.1
  78. */
  79. protected $set = null;
  80. /**
  81. * @var \Joomla\Database\Query\Element The where element.
  82. * @since 11.1
  83. */
  84. protected $where = null;
  85. /**
  86. * @var \Joomla\Database\Query\Element The group by element.
  87. * @since 11.1
  88. */
  89. protected $group = null;
  90. /**
  91. * @var \Joomla\Database\Query\Element The having element.
  92. * @since 11.1
  93. */
  94. protected $having = null;
  95. /**
  96. * @var \Joomla\Database\Query\Element The column list for an INSERT statement.
  97. * @since 11.1
  98. */
  99. protected $columns = null;
  100. /**
  101. * @var \Joomla\Database\Query\Element The values list for an INSERT statement.
  102. * @since 11.1
  103. */
  104. protected $values = null;
  105. /**
  106. * @var \Joomla\Database\Query\Element The order element.
  107. * @since 11.1
  108. */
  109. protected $order = null;
  110. /**
  111. * @var object The auto increment insert field element.
  112. * @since 11.1
  113. */
  114. protected $autoIncrementField = null;
  115. /**
  116. * @var \Joomla\Database\Query\Element The call element.
  117. * @since 12.1
  118. */
  119. protected $call = null;
  120. /**
  121. * @var \Joomla\Database\Query\Element The exec element.
  122. * @since 12.1
  123. */
  124. protected $exec = null;
  125. /**
  126. * @var \Joomla\Database\Query\Element The union element.
  127. * @since 12.1
  128. */
  129. protected $union = null;
  130. /**
  131. * Magic method to provide method alias support for quote() and quoteName().
  132. *
  133. * @param string $method The called method.
  134. * @param array $args The array of arguments passed to the method.
  135. *
  136. * @return string The aliased method's return value or null.
  137. *
  138. * @since 11.1
  139. */
  140. public function __call($method, $args)
  141. {
  142. if (empty($args))
  143. {
  144. return;
  145. }
  146. switch ($method)
  147. {
  148. case 'q':
  149. return $this->quote($args[0], isset($args[1]) ? $args[1] : true);
  150. break;
  151. case 'qn':
  152. return $this->quoteName($args[0], isset($args[1]) ? $args[1] : null);
  153. break;
  154. case 'e':
  155. return $this->escape($args[0], isset($args[1]) ? $args[1] : false);
  156. break;
  157. }
  158. }
  159. /**
  160. * Class constructor.
  161. *
  162. * @param Driver $db The database driver.
  163. *
  164. * @since 11.1
  165. */
  166. public function __construct(Driver $db = null)
  167. {
  168. $this->db = $db;
  169. }
  170. /**
  171. * Magic function to convert the query to a string.
  172. *
  173. * @return string The completed query.
  174. *
  175. * @since 11.1
  176. */
  177. public function __toString()
  178. {
  179. $query = '';
  180. if ($this->sql)
  181. {
  182. return $this->sql;
  183. }
  184. switch ($this->type)
  185. {
  186. case 'element':
  187. $query .= (string) $this->element;
  188. break;
  189. case 'select':
  190. $query .= (string) $this->select;
  191. $query .= (string) $this->from;
  192. if ($this->join)
  193. {
  194. // Special case for joins
  195. foreach ($this->join as $join)
  196. {
  197. $query .= (string) $join;
  198. }
  199. }
  200. if ($this->where)
  201. {
  202. $query .= (string) $this->where;
  203. }
  204. if ($this->group)
  205. {
  206. $query .= (string) $this->group;
  207. }
  208. if ($this->having)
  209. {
  210. $query .= (string) $this->having;
  211. }
  212. if ($this->order)
  213. {
  214. $query .= (string) $this->order;
  215. }
  216. break;
  217. case 'union':
  218. $query .= (string) $this->union;
  219. break;
  220. case 'delete':
  221. $query .= (string) $this->delete;
  222. $query .= (string) $this->from;
  223. if ($this->join)
  224. {
  225. // Special case for joins
  226. foreach ($this->join as $join)
  227. {
  228. $query .= (string) $join;
  229. }
  230. }
  231. if ($this->where)
  232. {
  233. $query .= (string) $this->where;
  234. }
  235. break;
  236. case 'update':
  237. $query .= (string) $this->update;
  238. if ($this->join)
  239. {
  240. // Special case for joins
  241. foreach ($this->join as $join)
  242. {
  243. $query .= (string) $join;
  244. }
  245. }
  246. $query .= (string) $this->set;
  247. if ($this->where)
  248. {
  249. $query .= (string) $this->where;
  250. }
  251. break;
  252. case 'insert':
  253. $query .= (string) $this->insert;
  254. // Set method
  255. if ($this->set)
  256. {
  257. $query .= (string) $this->set;
  258. }
  259. // Columns-Values method
  260. elseif ($this->values)
  261. {
  262. if ($this->columns)
  263. {
  264. $query .= (string) $this->columns;
  265. }
  266. $elements = $this->values->getElements();
  267. if (!($elements[0] instanceof $this))
  268. {
  269. $query .= ' VALUES ';
  270. }
  271. $query .= (string) $this->values;
  272. }
  273. break;
  274. case 'call':
  275. $query .= (string) $this->call;
  276. break;
  277. case 'exec':
  278. $query .= (string) $this->exec;
  279. break;
  280. }
  281. if ($this instanceof Query\Limitable)
  282. {
  283. $query = $this->processLimit($query, $this->limit, $this->offset);
  284. }
  285. return $query;
  286. }
  287. /**
  288. * Magic function to get protected variable value
  289. *
  290. * @param string $name The name of the variable.
  291. *
  292. * @return mixed
  293. *
  294. * @since 11.1
  295. */
  296. public function __get($name)
  297. {
  298. return isset($this->$name) ? $this->$name : null;
  299. }
  300. /**
  301. * Add a single column, or array of columns to the CALL clause of the query.
  302. *
  303. * Note that you must not mix insert, update, delete and select method calls when building a query.
  304. * The call method can, however, be called multiple times in the same query.
  305. *
  306. * Usage:
  307. * $query->call('a.*')->call('b.id');
  308. * $query->call(array('a.*', 'b.id'));
  309. *
  310. * @param mixed $columns A string or an array of field names.
  311. *
  312. * @return Query Returns this object to allow chaining.
  313. *
  314. * @since 12.1
  315. */
  316. public function call($columns)
  317. {
  318. $this->type = 'call';
  319. if (is_null($this->call))
  320. {
  321. $this->call = new Query\Element('CALL', $columns);
  322. }
  323. else
  324. {
  325. $this->call->append($columns);
  326. }
  327. return $this;
  328. }
  329. /**
  330. * Casts a value to a char.
  331. *
  332. * Ensure that the value is properly quoted before passing to the method.
  333. *
  334. * Usage:
  335. * $query->select($query->castAsChar('a'));
  336. *
  337. * @param string $value The value to cast as a char.
  338. *
  339. * @return string Returns the cast value.
  340. *
  341. * @since 11.1
  342. */
  343. public function castAsChar($value)
  344. {
  345. return $value;
  346. }
  347. /**
  348. * Gets the number of characters in a string.
  349. *
  350. * Note, use 'length' to find the number of bytes in a string.
  351. *
  352. * Usage:
  353. * $query->select($query->charLength('a'));
  354. *
  355. * @param string $field A value.
  356. * @param string $operator Comparison operator between charLength integer value and $condition
  357. * @param string $condition Integer value to compare charLength with.
  358. *
  359. * @return string The required char length call.
  360. *
  361. * @since 11.1
  362. */
  363. public function charLength($field, $operator = null, $condition = null)
  364. {
  365. return 'CHAR_LENGTH(' . $field . ')' . (isset($operator) && isset($condition) ? ' ' . $operator . ' ' . $condition : '');
  366. }
  367. /**
  368. * Clear data from the query or a specific clause of the query.
  369. *
  370. * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query.
  371. *
  372. * @return Query Returns this object to allow chaining.
  373. *
  374. * @since 11.1
  375. */
  376. public function clear($clause = null)
  377. {
  378. $this->sql = null;
  379. switch ($clause)
  380. {
  381. case 'select':
  382. $this->select = null;
  383. $this->type = null;
  384. break;
  385. case 'delete':
  386. $this->delete = null;
  387. $this->type = null;
  388. break;
  389. case 'update':
  390. $this->update = null;
  391. $this->type = null;
  392. break;
  393. case 'insert':
  394. $this->insert = null;
  395. $this->type = null;
  396. $this->autoIncrementField = null;
  397. break;
  398. case 'from':
  399. $this->from = null;
  400. break;
  401. case 'join':
  402. $this->join = null;
  403. break;
  404. case 'set':
  405. $this->set = null;
  406. break;
  407. case 'where':
  408. $this->where = null;
  409. break;
  410. case 'group':
  411. $this->group = null;
  412. break;
  413. case 'having':
  414. $this->having = null;
  415. break;
  416. case 'order':
  417. $this->order = null;
  418. break;
  419. case 'columns':
  420. $this->columns = null;
  421. break;
  422. case 'values':
  423. $this->values = null;
  424. break;
  425. case 'exec':
  426. $this->exec = null;
  427. $this->type = null;
  428. break;
  429. case 'call':
  430. $this->call = null;
  431. $this->type = null;
  432. break;
  433. case 'limit':
  434. $this->offset = 0;
  435. $this->limit = 0;
  436. break;
  437. case 'union':
  438. $this->union = null;
  439. break;
  440. default:
  441. $this->type = null;
  442. $this->select = null;
  443. $this->delete = null;
  444. $this->update = null;
  445. $this->insert = null;
  446. $this->from = null;
  447. $this->join = null;
  448. $this->set = null;
  449. $this->where = null;
  450. $this->group = null;
  451. $this->having = null;
  452. $this->order = null;
  453. $this->columns = null;
  454. $this->values = null;
  455. $this->autoIncrementField = null;
  456. $this->exec = null;
  457. $this->call = null;
  458. $this->union = null;
  459. $this->offset = 0;
  460. $this->limit = 0;
  461. break;
  462. }
  463. return $this;
  464. }
  465. /**
  466. * Adds a column, or array of column names that would be used for an INSERT INTO statement.
  467. *
  468. * @param mixed $columns A column name, or array of column names.
  469. *
  470. * @return Query Returns this object to allow chaining.
  471. *
  472. * @since 11.1
  473. */
  474. public function columns($columns)
  475. {
  476. if (is_null($this->columns))
  477. {
  478. $this->columns = new Query\Element('()', $columns);
  479. }
  480. else
  481. {
  482. $this->columns->append($columns);
  483. }
  484. return $this;
  485. }
  486. /**
  487. * Concatenates an array of column names or values.
  488. *
  489. * Usage:
  490. * $query->select($query->concatenate(array('a', 'b')));
  491. *
  492. * @param array $values An array of values to concatenate.
  493. * @param string $separator As separator to place between each value.
  494. *
  495. * @return string The concatenated values.
  496. *
  497. * @since 11.1
  498. */
  499. public function concatenate($values, $separator = null)
  500. {
  501. if ($separator)
  502. {
  503. return 'CONCATENATE(' . implode(' || ' . $this->quote($separator) . ' || ', $values) . ')';
  504. }
  505. else
  506. {
  507. return 'CONCATENATE(' . implode(' || ', $values) . ')';
  508. }
  509. }
  510. /**
  511. * Gets the current date and time.
  512. *
  513. * Usage:
  514. * $query->where('published_up < '.$query->currentTimestamp());
  515. *
  516. * @return string
  517. *
  518. * @since 11.1
  519. */
  520. public function currentTimestamp()
  521. {
  522. return 'CURRENT_TIMESTAMP()';
  523. }
  524. /**
  525. * Returns a PHP date() function compliant date format for the database driver.
  526. *
  527. * This method is provided for use where the query object is passed to a function for modification.
  528. * If you have direct access to the database object, it is recommended you use the getDateFormat method directly.
  529. *
  530. * @return string The format string.
  531. *
  532. * @since 11.1
  533. */
  534. public function dateFormat()
  535. {
  536. if (!($this->db instanceof Driver))
  537. {
  538. throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
  539. }
  540. return $this->db->getDateFormat();
  541. }
  542. /**
  543. * Creates a formatted dump of the query for debugging purposes.
  544. *
  545. * Usage:
  546. * echo $query->dump();
  547. *
  548. * @return string
  549. *
  550. * @since 11.3
  551. */
  552. public function dump()
  553. {
  554. return '<pre class="jdatabasequery">' . str_replace('#__', $this->db->getPrefix(), $this) . '</pre>';
  555. }
  556. /**
  557. * Add a table name to the DELETE clause of the query.
  558. *
  559. * Note that you must not mix insert, update, delete and select method calls when building a query.
  560. *
  561. * Usage:
  562. * $query->delete('#__a')->where('id = 1');
  563. *
  564. * @param string $table The name of the table to delete from.
  565. *
  566. * @return Query Returns this object to allow chaining.
  567. *
  568. * @since 11.1
  569. */
  570. public function delete($table = null)
  571. {
  572. $this->type = 'delete';
  573. $this->delete = new Query\Element('DELETE', null);
  574. if (!empty($table))
  575. {
  576. $this->from($table);
  577. }
  578. return $this;
  579. }
  580. /**
  581. * Method to escape a string for usage in an SQL statement.
  582. *
  583. * This method is provided for use where the query object is passed to a function for modification.
  584. * If you have direct access to the database object, it is recommended you use the escape method directly.
  585. *
  586. * Note that 'e' is an alias for this method as it is in JDatabaseDriver.
  587. *
  588. * @param string $text The string to be escaped.
  589. * @param boolean $extra Optional parameter to provide extra escaping.
  590. *
  591. * @return string The escaped string.
  592. *
  593. * @since 11.1
  594. * @throws RuntimeException if the internal db property is not a valid object.
  595. */
  596. public function escape($text, $extra = false)
  597. {
  598. if (!($this->db instanceof Driver))
  599. {
  600. throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
  601. }
  602. return $this->db->escape($text, $extra);
  603. }
  604. /**
  605. * Add a single column, or array of columns to the EXEC clause of the query.
  606. *
  607. * Note that you must not mix insert, update, delete and select method calls when building a query.
  608. * The exec method can, however, be called multiple times in the same query.
  609. *
  610. * Usage:
  611. * $query->exec('a.*')->exec('b.id');
  612. * $query->exec(array('a.*', 'b.id'));
  613. *
  614. * @param mixed $columns A string or an array of field names.
  615. *
  616. * @return Query Returns this object to allow chaining.
  617. *
  618. * @since 12.1
  619. */
  620. public function exec($columns)
  621. {
  622. $this->type = 'exec';
  623. if (is_null($this->exec))
  624. {
  625. $this->exec = new Query\Element('EXEC', $columns);
  626. }
  627. else
  628. {
  629. $this->exec->append($columns);
  630. }
  631. return $this;
  632. }
  633. /**
  634. * Add a table to the FROM clause of the query.
  635. *
  636. * Note that while an array of tables can be provided, it is recommended you use explicit joins.
  637. *
  638. * Usage:
  639. * $query->select('*')->from('#__a');
  640. *
  641. * @param mixed $tables A string or array of table names.
  642. * This can be a JDatabaseQuery object (or a child of it) when used
  643. * as a subquery in FROM clause along with a value for $subQueryAlias.
  644. * @param string $subQueryAlias Alias used when $tables is a JDatabaseQuery.
  645. *
  646. * @return Query Returns this object to allow chaining.
  647. *
  648. * @since 11.1
  649. */
  650. public function from($tables, $subQueryAlias = null)
  651. {
  652. if (is_null($this->from))
  653. {
  654. if ($tables instanceof $this)
  655. {
  656. if (is_null($subQueryAlias))
  657. {
  658. throw new RuntimeException('JLIB_DATABASE_ERROR_NULL_SUBQUERY_ALIAS');
  659. }
  660. $tables = '( ' . (string) $tables . ' ) AS ' . $this->quoteName($subQueryAlias);
  661. }
  662. $this->from = new Query\Element('FROM', $tables);
  663. }
  664. else
  665. {
  666. $this->from->append($tables);
  667. }
  668. return $this;
  669. }
  670. /**
  671. * Used to get a string to extract year from date column.
  672. *
  673. * Usage:
  674. * $query->select($query->year($query->quoteName('dateColumn')));
  675. *
  676. * @param string $date Date column containing year to be extracted.
  677. *
  678. * @return string Returns string to extract year from a date.
  679. *
  680. * @since 12.1
  681. */
  682. public function year($date)
  683. {
  684. return 'YEAR(' . $date . ')';
  685. }
  686. /**
  687. * Used to get a string to extract month from date column.
  688. *
  689. * Usage:
  690. * $query->select($query->month($query->quoteName('dateColumn')));
  691. *
  692. * @param string $date Date column containing month to be extracted.
  693. *
  694. * @return string Returns string to extract month from a date.
  695. *
  696. * @since 12.1
  697. */
  698. public function month($date)
  699. {
  700. return 'MONTH(' . $date . ')';
  701. }
  702. /**
  703. * Used to get a string to extract day from date column.
  704. *
  705. * Usage:
  706. * $query->select($query->day($query->quoteName('dateColumn')));
  707. *
  708. * @param string $date Date column containing day to be extracted.
  709. *
  710. * @return string Returns string to extract day from a date.
  711. *
  712. * @since 12.1
  713. */
  714. public function day($date)
  715. {
  716. return 'DAY(' . $date . ')';
  717. }
  718. /**
  719. * Used to get a string to extract hour from date column.
  720. *
  721. * Usage:
  722. * $query->select($query->hour($query->quoteName('dateColumn')));
  723. *
  724. * @param string $date Date column containing hour to be extracted.
  725. *
  726. * @return string Returns string to extract hour from a date.
  727. *
  728. * @since 12.1
  729. */
  730. public function hour($date)
  731. {
  732. return 'HOUR(' . $date . ')';
  733. }
  734. /**
  735. * Used to get a string to extract minute from date column.
  736. *
  737. * Usage:
  738. * $query->select($query->minute($query->quoteName('dateColumn')));
  739. *
  740. * @param string $date Date column containing minute to be extracted.
  741. *
  742. * @return string Returns string to extract minute from a date.
  743. *
  744. * @since 12.1
  745. */
  746. public function minute($date)
  747. {
  748. return 'MINUTE(' . $date . ')';
  749. }
  750. /**
  751. * Used to get a string to extract seconds from date column.
  752. *
  753. * Usage:
  754. * $query->select($query->second($query->quoteName('dateColumn')));
  755. *
  756. * @param string $date Date column containing second to be extracted.
  757. *
  758. * @return string Returns string to extract second from a date.
  759. *
  760. * @since 12.1
  761. */
  762. public function second($date)
  763. {
  764. return 'SECOND(' . $date . ')';
  765. }
  766. /**
  767. * Add a grouping column to the GROUP clause of the query.
  768. *
  769. * Usage:
  770. * $query->group('id');
  771. *
  772. * @param mixed $columns A string or array of ordering columns.
  773. *
  774. * @return Query Returns this object to allow chaining.
  775. *
  776. * @since 11.1
  777. */
  778. public function group($columns)
  779. {
  780. if (is_null($this->group))
  781. {
  782. $this->group = new Query\Element('GROUP BY', $columns);
  783. }
  784. else
  785. {
  786. $this->group->append($columns);
  787. }
  788. return $this;
  789. }
  790. /**
  791. * A conditions to the HAVING clause of the query.
  792. *
  793. * Usage:
  794. * $query->group('id')->having('COUNT(id) > 5');
  795. *
  796. * @param mixed $conditions A string or array of columns.
  797. * @param string $glue The glue by which to join the conditions. Defaults to AND.
  798. *
  799. * @return Query Returns this object to allow chaining.
  800. *
  801. * @since 11.1
  802. */
  803. public function having($conditions, $glue = 'AND')
  804. {
  805. if (is_null($this->having))
  806. {
  807. $glue = strtoupper($glue);
  808. $this->having = new Query\Element('HAVING', $conditions, " $glue ");
  809. }
  810. else
  811. {
  812. $this->having->append($conditions);
  813. }
  814. return $this;
  815. }
  816. /**
  817. * Add an INNER JOIN clause to the query.
  818. *
  819. * Usage:
  820. * $query->innerJoin('b ON b.id = a.id')->innerJoin('c ON c.id = b.id');
  821. *
  822. * @param string $condition The join condition.
  823. *
  824. * @return Query Returns this object to allow chaining.
  825. *
  826. * @since 11.1
  827. */
  828. public function innerJoin($condition)
  829. {
  830. $this->join('INNER', $condition);
  831. return $this;
  832. }
  833. /**
  834. * Add a table name to the INSERT clause of the query.
  835. *
  836. * Note that you must not mix insert, update, delete and select method calls when building a query.
  837. *
  838. * Usage:
  839. * $query->insert('#__a')->set('id = 1');
  840. * $query->insert('#__a')->columns('id, title')->values('1,2')->values('3,4');
  841. * $query->insert('#__a')->columns('id, title')->values(array('1,2', '3,4'));
  842. *
  843. * @param mixed $table The name of the table to insert data into.
  844. * @param boolean $incrementField The name of the field to auto increment.
  845. *
  846. * @return Query Returns this object to allow chaining.
  847. *
  848. * @since 11.1
  849. */
  850. public function insert($table, $incrementField=false)
  851. {
  852. $this->type = 'insert';
  853. $this->insert = new Query\Element('INSERT INTO', $table);
  854. $this->autoIncrementField = $incrementField;
  855. return $this;
  856. }
  857. /**
  858. * Add a JOIN clause to the query.
  859. *
  860. * Usage:
  861. * $query->join('INNER', 'b ON b.id = a.id);
  862. *
  863. * @param string $type The type of join. This string is prepended to the JOIN keyword.
  864. * @param string $conditions A string or array of conditions.
  865. *
  866. * @return Query Returns this object to allow chaining.
  867. *
  868. * @since 11.1
  869. */
  870. public function join($type, $conditions)
  871. {
  872. if (is_null($this->join))
  873. {
  874. $this->join = array();
  875. }
  876. $this->join[] = new Query\Element(strtoupper($type) . ' JOIN', $conditions);
  877. return $this;
  878. }
  879. /**
  880. * Add a LEFT JOIN clause to the query.
  881. *
  882. * Usage:
  883. * $query->leftJoin('b ON b.id = a.id')->leftJoin('c ON c.id = b.id');
  884. *
  885. * @param string $condition The join condition.
  886. *
  887. * @return Query Returns this object to allow chaining.
  888. *
  889. * @since 11.1
  890. */
  891. public function leftJoin($condition)
  892. {
  893. $this->join('LEFT', $condition);
  894. return $this;
  895. }
  896. /**
  897. * Get the length of a string in bytes.
  898. *
  899. * Note, use 'charLength' to find the number of characters in a string.
  900. *
  901. * Usage:
  902. * query->where($query->length('a').' > 3');
  903. *
  904. * @param string $value The string to measure.
  905. *
  906. * @return int
  907. *
  908. * @since 11.1
  909. */
  910. public function length($value)
  911. {
  912. return 'LENGTH(' . $value . ')';
  913. }
  914. /**
  915. * Get the null or zero representation of a timestamp for the database driver.
  916. *
  917. * This method is provided for use where the query object is passed to a function for modification.
  918. * If you have direct access to the database object, it is recommended you use the nullDate method directly.
  919. *
  920. * Usage:
  921. * $query->where('modified_date <> '.$query->nullDate());
  922. *
  923. * @param boolean $quoted Optionally wraps the null date in database quotes (true by default).
  924. *
  925. * @return string Null or zero representation of a timestamp.
  926. *
  927. * @since 11.1
  928. */
  929. public function nullDate($quoted = true)
  930. {
  931. if (!($this->db instanceof Driver))
  932. {
  933. throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
  934. }
  935. $result = $this->db->getNullDate($quoted);
  936. if ($quoted)
  937. {
  938. return $this->db->quote($result);
  939. }
  940. return $result;
  941. }
  942. /**
  943. * Add a ordering column to the ORDER clause of the query.
  944. *
  945. * Usage:
  946. * $query->order('foo')->order('bar');
  947. * $query->order(array('foo','bar'));
  948. *
  949. * @param mixed $columns A string or array of ordering columns.
  950. *
  951. * @return Query Returns this object to allow chaining.
  952. *
  953. * @since 11.1
  954. */
  955. public function order($columns)
  956. {
  957. if (is_null($this->order))
  958. {
  959. $this->order = new Query\Element('ORDER BY', $columns);
  960. }
  961. else
  962. {
  963. $this->order->append($columns);
  964. }
  965. return $this;
  966. }
  967. /**
  968. * Add an OUTER JOIN clause to the query.
  969. *
  970. * Usage:
  971. * $query->outerJoin('b ON b.id = a.id')->outerJoin('c ON c.id = b.id');
  972. *
  973. * @param string $condition The join condition.
  974. *
  975. * @return Query Returns this object to allow chaining.
  976. *
  977. * @since 11.1
  978. */
  979. public function outerJoin($condition)
  980. {
  981. $this->join('OUTER', $condition);
  982. return $this;
  983. }
  984. /**
  985. * Method to quote and optionally escape a string to database requirements for insertion into the database.
  986. *
  987. * This method is provided for use where the query object is passed to a function for modification.
  988. * If you have direct access to the database object, it is recommended you use the quote method directly.
  989. *
  990. * Note that 'q' is an alias for this method as it is in JDatabaseDriver.
  991. *
  992. * Usage:
  993. * $query->quote('fulltext');
  994. * $query->q('fulltext');
  995. * $query->q(array('option', 'fulltext'));
  996. *
  997. * @param mixed $text A string or an array of strings to quote.
  998. * @param boolean $escape True to escape the string, false to leave it unchanged.
  999. *
  1000. * @return string The quoted input string.
  1001. *
  1002. * @since 11.1
  1003. * @throws RuntimeException if the internal db property is not a valid object.
  1004. */
  1005. public function quote($text, $escape = true)
  1006. {
  1007. if (!($this->db instanceof Driver))
  1008. {
  1009. throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
  1010. }
  1011. return $this->db->quote($text, $escape);
  1012. }
  1013. /**
  1014. * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection
  1015. * risks and reserved word conflicts.
  1016. *
  1017. * This method is provided for use where the query object is passed to a function for modification.
  1018. * If you have direct access to the database object, it is recommended you use the quoteName method directly.
  1019. *
  1020. * Note that 'qn' is an alias for this method as it is in JDatabaseDriver.
  1021. *
  1022. * Usage:
  1023. * $query->quoteName('#__a');
  1024. * $query->qn('#__a');
  1025. *
  1026. * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
  1027. * Each type supports dot-notation name.
  1028. * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be
  1029. * same length of $name; if is null there will not be any AS part for string or array element.
  1030. *
  1031. * @return mixed The quote wrapped name, same type of $name.
  1032. *
  1033. * @since 11.1
  1034. * @throws RuntimeException if the internal db property is not a valid object.
  1035. */
  1036. public function quoteName($name, $as = null)
  1037. {
  1038. if (!($this->db instanceof Driver))
  1039. {
  1040. throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
  1041. }
  1042. return $this->db->quoteName($name, $as);
  1043. }
  1044. /**
  1045. * Add a RIGHT JOIN clause to the query.
  1046. *
  1047. * Usage:
  1048. * $query->rightJoin('b ON b.id = a.id')->rightJoin('c ON c.id = b.id');
  1049. *
  1050. * @param string $condition The join condition.
  1051. *
  1052. * @return Query Returns this object to allow chaining.
  1053. *
  1054. * @since 11.1
  1055. */
  1056. public function rightJoin($condition)
  1057. {
  1058. $this->join('RIGHT', $condition);
  1059. return $this;
  1060. }
  1061. /**
  1062. * Add a single column, or array of columns to the SELECT clause of the query.
  1063. *
  1064. * Note that you must not mix insert, update, delete and select method calls when building a query.
  1065. * The select method can, however, be called multiple times in the same query.
  1066. *
  1067. * Usage:
  1068. * $query->select('a.*')->select('b.id');
  1069. * $query->select(array('a.*', 'b.id'));
  1070. *
  1071. * @param mixed $columns A string or an array of field names.
  1072. *
  1073. * @return Query Returns this object to allow chaining.
  1074. *
  1075. * @since 11.1
  1076. */
  1077. public function select($columns)
  1078. {
  1079. $this->type = 'select';
  1080. if (is_null($this->select))
  1081. {
  1082. $this->select = new Query\Element('SELECT', $columns);
  1083. }
  1084. else
  1085. {
  1086. $this->select->append($columns);
  1087. }
  1088. return $this;
  1089. }
  1090. /**
  1091. * Add a single condition string, or an array of strings to the SET clause of the query.
  1092. *
  1093. * Usage:
  1094. * $query->set('a = 1')->set('b = 2');
  1095. * $query->set(array('a = 1', 'b = 2');
  1096. *
  1097. * @param mixed $conditions A string or array of string conditions.
  1098. * @param string $glue The glue by which to join the condition strings. Defaults to ,.
  1099. * Note that the glue is set on first use and cannot be changed.
  1100. *
  1101. * @return Query Returns this object to allow chaining.
  1102. *
  1103. * @since 11.1
  1104. */
  1105. public function set($conditions, $glue = ',')
  1106. {
  1107. if (is_null($this->set))
  1108. {
  1109. $glue = strtoupper($glue);
  1110. $this->set = new Query\Element('SET', $conditions, "\n\t$glue ");
  1111. }
  1112. else
  1113. {
  1114. $this->set->append($conditions);
  1115. }
  1116. return $this;
  1117. }
  1118. /**
  1119. * Allows a direct query to be provided to the database
  1120. * driver's setQuery() method, but still allow queries
  1121. * to have bounded variables.
  1122. *
  1123. * Usage:
  1124. * $query->setQuery('select * from #__users');
  1125. *
  1126. * @param mixed $sql An SQL Query
  1127. *
  1128. * @return Query Returns this object to allow chaining.
  1129. *
  1130. * @since 12.1
  1131. */
  1132. public function setQuery($sql)
  1133. {
  1134. $this->sql = $sql;
  1135. return $this;
  1136. }
  1137. /**
  1138. * Add a table name to the UPDATE clause of the query.
  1139. *
  1140. * Note that you must not mix insert, update, delete and select method calls when building a query.
  1141. *
  1142. * Usage:
  1143. * $query->update('#__foo')->set(...);
  1144. *
  1145. * @param string $table A table to update.
  1146. *
  1147. * @return Query Returns this object to allow chaining.
  1148. *
  1149. * @since 11.1
  1150. */
  1151. public function update($table)
  1152. {
  1153. $this->type = 'update';
  1154. $this->update = new Query\Element('UPDATE', $table);
  1155. return $this;
  1156. }
  1157. /**
  1158. * Adds a tuple, or array of tuples that would be used as values for an INSERT INTO statement.
  1159. *
  1160. * Usage:
  1161. * $query->values('1,2,3')->values('4,5,6');
  1162. * $query->values(array('1,2,3', '4,5,6'));
  1163. *
  1164. * @param string $values A single tuple, or array of tuples.
  1165. *
  1166. * @return Query Returns this object to allow chaining.
  1167. *
  1168. * @since 11.1
  1169. */
  1170. public function values($values)
  1171. {
  1172. if (is_null($this->values))
  1173. {
  1174. $this->values = new Query\Element('()', $values, '),(');
  1175. }
  1176. else
  1177. {
  1178. $this->values->append($values);
  1179. }
  1180. return $this;
  1181. }
  1182. /**
  1183. * Add a single condition, or an array of conditions to the WHERE clause of the query.
  1184. *
  1185. * Usage:
  1186. * $query->where('a = 1')->where('b = 2');
  1187. * $query->where(array('a = 1', 'b = 2'));
  1188. *
  1189. * @param mixed $conditions A string or array of where conditions.
  1190. * @param string $glue The glue by which to join the conditions. Defaults to AND.
  1191. * Note that the glue is set on first use and cannot be changed.
  1192. *
  1193. * @return Query Returns this object to allow chaining.
  1194. *
  1195. * @since 11.1
  1196. */
  1197. public function where($conditions, $glue = 'AND')
  1198. {
  1199. if (is_null($this->where))
  1200. {
  1201. $glue = strtoupper($glue);
  1202. $this->where = new Query\Element('WHERE', $conditions, " $glue ");
  1203. }
  1204. else
  1205. {
  1206. $this->where->append($conditions);
  1207. }
  1208. return $this;
  1209. }
  1210. /**
  1211. * Method to provide deep copy support to nested objects and
  1212. * arrays when cloning.
  1213. *
  1214. * @return void
  1215. *
  1216. * @since 11.3
  1217. */
  1218. public function __clone()
  1219. {
  1220. foreach ($this as $k => $v)
  1221. {
  1222. if (is_object($v) || is_array($v))
  1223. {
  1224. $this->{$k} = unserialize(serialize($v));
  1225. }
  1226. }
  1227. }
  1228. /**
  1229. * Add a query to UNION with the current query.
  1230. * Multiple unions each require separate statements and create an array of unions.
  1231. *
  1232. * Usage:
  1233. * $query->union('SELECT name FROM #__foo')
  1234. * $query->union('SELECT name FROM #__foo','distinct')
  1235. * $query->union(array('SELECT name FROM #__foo','SELECT name FROM #__bar'))
  1236. *
  1237. * @param mixed $query The JDatabaseQuery object or string to union.
  1238. * @param boolean $distinct True to only return distinct rows from the union.
  1239. * @param string $glue The glue by which to join the conditions.
  1240. *
  1241. * @return mixed The JDatabaseQuery object on success or boolean false on failure.
  1242. *
  1243. * @since 12.1
  1244. */
  1245. public function union($query, $distinct = false, $glue = '')
  1246. {
  1247. // Clear any ORDER BY clause in UNION query
  1248. // See http://dev.mysql.com/doc/refman/5.0/en/union.html
  1249. if (!is_null($this->order))
  1250. {
  1251. $this->clear('order');
  1252. }
  1253. // Set up the DISTINCT flag, the name with parentheses, and the glue.
  1254. if ($distinct)
  1255. {
  1256. $name = 'UNION DISTINCT ()';
  1257. $glue = ')' . PHP_EOL . 'UNION DISTINCT (';
  1258. }
  1259. else
  1260. {
  1261. $glue = ')' . PHP_EOL . 'UNION (';
  1262. $name = 'UNION ()';
  1263. }
  1264. // Get the JDatabaseQueryElement if it does not exist
  1265. if (is_null($this->union))
  1266. {
  1267. $this->union = new Query\Element($name, $query, "$glue");
  1268. }
  1269. // Otherwise append the second UNION.
  1270. else
  1271. {
  1272. $glue = '';
  1273. $this->union->append($query);
  1274. }
  1275. return $this;
  1276. }
  1277. /**
  1278. * Add a query to UNION DISTINCT with the current query. Simply a proxy to Union with the Distinct clause.
  1279. *
  1280. * Usage:
  1281. * $query->unionDistinct('SELECT name FROM #__foo')
  1282. *
  1283. * @param mixed $query The JDatabaseQuery object or string to union.
  1284. * @param string $glue The glue by which to join the conditions.
  1285. *
  1286. * @return mixed The JDatabaseQuery object on success or boolean false on failure.
  1287. *
  1288. * @since 12.1
  1289. */
  1290. public function unionDistinct($query, $glue = '')
  1291. {
  1292. $distinct = true;
  1293. // Apply the distinct flag to the union.
  1294. return $this->union($query, $distinct, $glue);
  1295. }
  1296. /**
  1297. * Find and replace sprintf-like tokens in a format string.
  1298. * Each token takes one of the following forms:
  1299. * %% - A literal percent character.
  1300. * %[t] - Where [t] is a type specifier.
  1301. * %[n]$[x] - Where [n] is an argument specifier and [t] is a type specifier.
  1302. *
  1303. * Types:
  1304. * a - Numeric: Replacement text is coerced to a numeric type but not quoted or escaped.
  1305. * e - Escape: Replacement text is passed to $this->escape().
  1306. * E - Escape (extra): Replacement text is passed to $this->escape() with true as the second argument.
  1307. * n - Name Quote: Replacement text is passed to $this->quoteName().
  1308. * q - Quote: Replacement text is passed to $this->quote().
  1309. * Q - Quote (no escape): Replacement text is passed to $this->quote() with false as the second argument.
  1310. * r - Raw: Replacement text is used as-is. (Be careful)
  1311. *
  1312. * Date Types:
  1313. * - Replacement text automatically quoted (use uppercase for Name Quote).
  1314. * - Replacement text should be a string in date format or name of a date column.
  1315. * y/Y - Year
  1316. * m/M - Month
  1317. * d/D - Day
  1318. * h/H - Hour
  1319. * i/I - Minute
  1320. * s/S - Second
  1321. *
  1322. * Invariable Types:
  1323. * - Takes no argument.
  1324. * - Argument index not incremented.
  1325. * t - Replacement text is the result of $this->currentTimestamp().
  1326. * z - Replacement text is the result of $this->nullDate(false).
  1327. * Z - Replacement text is the result of $this->nullDate(true).
  1328. *
  1329. * Usage:
  1330. * $query->format('SELECT %1$n FROM %2$n WHERE %3$n = %4$a', 'foo', '#__foo', 'bar', 1);
  1331. * Returns: SELECT `foo` FROM `#__foo` WHERE `bar` = 1
  1332. *
  1333. * Notes:
  1334. * The argument specifier is optional but recommended for clarity.
  1335. * The argument index used for unspecified tokens is incremented only when used.
  1336. *
  1337. * @param string $format The formatting string.
  1338. *
  1339. * @return string Returns a string produced according to the formatting string.
  1340. *
  1341. * @since 12.3
  1342. */
  1343. public function format($format)
  1344. {
  1345. $query = $this;
  1346. $args = array_slice(func_get_args(), 1);
  1347. array_unshift($args, null);
  1348. $i = 1;
  1349. $func = function ($match) use ($query, $args, &$i)
  1350. {
  1351. if (isset($match[6]) && $match[6] == '%')
  1352. {
  1353. return '%';
  1354. }
  1355. // No argument required, do not increment the argument index.
  1356. switch ($match[5])
  1357. {
  1358. case 't':
  1359. return $query->currentTimestamp();
  1360. break;
  1361. case 'z':
  1362. return $query->nullDate(false);
  1363. break;
  1364. case 'Z':
  1365. return $query->nullDate(true);
  1366. break;
  1367. }
  1368. // Increment the argument index only if argument specifier not provided.
  1369. $index = is_numeric($match[4]) ? (int) $match[4] : $i++;
  1370. if (!$index || !isset($args[$index]))
  1371. {
  1372. // TODO - What to do? sprintf() throws a Warning in these cases.
  1373. $replacement = '';
  1374. }
  1375. else
  1376. {
  1377. $replacement = $args[$index];
  1378. }
  1379. switch ($match[5])
  1380. {
  1381. case 'a':
  1382. return 0 + $replacement;
  1383. break;
  1384. case 'e':
  1385. return $query->escape($replacement);
  1386. break;
  1387. case 'E':
  1388. return $query->escape($replacement, true);
  1389. break;
  1390. case 'n':
  1391. return $query->quoteName($replacement);
  1392. break;
  1393. case 'q':
  1394. return $query->quote($replacement);
  1395. break;
  1396. case 'Q':
  1397. return $query->quote($replacement, false);
  1398. break;
  1399. case 'r':
  1400. return $replacement;
  1401. break;
  1402. // Dates
  1403. case 'y':
  1404. return $query->year($query->quote($replacement));
  1405. break;
  1406. case 'Y':
  1407. return $query->year($query->quoteName($replacement));
  1408. break;
  1409. case 'm':
  1410. return $query->month($query->quote($replacement));
  1411. break;
  1412. case 'M':
  1413. return $query->month($query->quoteName($replacement));
  1414. break;
  1415. case 'd':
  1416. return $query->day($query->quote($replacement));
  1417. break;
  1418. case 'D':
  1419. return $query->day($query->quoteName($replacement));
  1420. break;
  1421. case 'h':
  1422. return $query->hour($query->quote($replacement));
  1423. break;
  1424. case 'H':
  1425. return $query->hour($query->quoteName($replacement));
  1426. break;
  1427. case 'i':
  1428. return $query->minute($query->quote($replacement));
  1429. break;
  1430. case 'I':
  1431. return $query->minute($query->quoteName($replacement));
  1432. break;
  1433. case 's':
  1434. return $query->second($query->quote($replacement));
  1435. break;
  1436. case 'S':
  1437. return $query->second($query->quoteName($replacement));
  1438. break;
  1439. }
  1440. return '';
  1441. };
  1442. /**
  1443. * Regexp to find an replace all tokens.
  1444. * Matched fields:
  1445. * 0: Full token
  1446. * 1: Everything following '%'
  1447. * 2: Everything following '%' unless '%'
  1448. * 3: Argument specifier and '$'
  1449. * 4: Argument specifier
  1450. * 5: Type specifier
  1451. * 6: '%' if full token is '%%'
  1452. */
  1453. return preg_replace_callback('#%(((([\d]+)\$)?([aeEnqQryYmMdDhHiIsStzZ]))|(%))#', $func, $format);
  1454. }
  1455. }