pquery.sql.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. <?php
  2. /**
  3. * pQuery plugin for executing MySQL queries.
  4. *
  5. * @package pQuery
  6. */
  7. /**
  8. * @todo Documentation
  9. * @property $query The query that is being evaluated.
  10. */
  11. class pQuerySql extends pQuery implements pQueryExtension {
  12. const VARIABLE_PATTERN = '/\[\s*%s\s*\]/';
  13. static $accepts = array('string' => 'parse_query', 'resource');
  14. /**
  15. * The default row fetching type, one of 'assoc', 'object' or 'array'.
  16. *
  17. * @var resource
  18. */
  19. const DEFAULT_FETCH_TYPE = 'assoc';
  20. /**
  21. * The MySQL link identifier.
  22. *
  23. * @var resource
  24. */
  25. static $link;
  26. /**
  27. * @see pQuery::$variable_alias
  28. * @var string|array
  29. */
  30. static $variable_alias = 'query';
  31. /**
  32. * Database login data, should be an associative array containing
  33. * values for 'host', 'username', 'password' and 'dbname'
  34. *
  35. * @var array
  36. */
  37. static $login_data = array();
  38. /**
  39. * The result of the current query.
  40. *
  41. * @var resource|bool
  42. */
  43. var $result;
  44. /**
  45. * Indicates whether the query has been executed yet.
  46. *
  47. * @var bool
  48. */
  49. var $executed;
  50. /**
  51. * Parse the given query string.
  52. */
  53. function parse_query() {
  54. $args = $this->arguments;
  55. if( !count($args) )
  56. return;
  57. // Replace variable indices by names equal to their indices
  58. if( !is_array($args[0]) )
  59. array_unshift($args, null);
  60. // Replace variables by their escaped values
  61. $this->set($args);
  62. }
  63. /**
  64. * Replace a set of variables with their (optionally escaped)
  65. * values in the current query.
  66. *
  67. * @param array $variables The variables to replace.
  68. * @param bool $escape Whether to escape the variable values.
  69. * @returns pQuerySql The current query object.
  70. */
  71. function replace_variables($variables, $escape) {
  72. $patterns = array_map('pQuerySql::variable_pattern', array_keys($variables));
  73. $escape && $variables = array_map('pQuerySql::escape', $variables);
  74. $this->variable = preg_replace($patterns, $variables, $this->variable);
  75. $this->executed = false;
  76. return $this;
  77. }
  78. /**
  79. * Replace a set of variables with their escaped values in the current query.
  80. *
  81. * @param array $variables The variables to replace.
  82. * @returns pQuerySql The current query object.
  83. */
  84. function set($variables) {
  85. return $this->replace_variables($variables, true);
  86. }
  87. /**
  88. * Replace a set of variables with their non-escaped values in the current query.
  89. *
  90. * @param array $variables The variables to replace.
  91. * @returns pQuerySql The current query object.
  92. */
  93. function set_unescaped($variables) {
  94. return $this->replace_variables($variables, false);
  95. }
  96. /**
  97. * Transform a variable name to a regex to be used as a replacement
  98. * pattern in a query.
  99. *
  100. * @param string $name The variable name to transform.
  101. * @returns string The variable's replacement pattern.
  102. */
  103. static function variable_pattern($name) {
  104. return sprintf(self::VARIABLE_PATTERN, $name);
  105. }
  106. /**
  107. * Execute the current query.
  108. *
  109. * @returns pQuerySql The current query object.
  110. */
  111. function execute() {
  112. self::assert_connection();
  113. $result = mysql_query($this->query, self::$link);
  114. if( !$result )
  115. return self::mysql_error();
  116. $this->result = $result;
  117. $this->executed = true;
  118. return $this;
  119. }
  120. /**
  121. * Fetch a row from the current result.
  122. *
  123. * @param string $type The format of the result row.
  124. * @returns mixed The fetched row in the requested format.
  125. */
  126. function fetch($type=self::DEFAULT_FETCH_TYPE) {
  127. $this->assert_execution();
  128. if( !$this->result )
  129. return self::error('No valid result to fetch from.');
  130. $func = 'mysql_fetch_'.$type;
  131. if( !function_exists($func) )
  132. return self::error('Fetch type "%s" is not supported.', $type);
  133. return $func($this->result);
  134. }
  135. /**
  136. * Fetch all rows from the current result.
  137. *
  138. * @param string $type The format of the result rows.
  139. * @returns array The result set.
  140. */
  141. function fetch_all($type=self::DEFAULT_FETCH_TYPE) {
  142. $results = array();
  143. while( ($row = $this->fetch($type)) !== false ) {
  144. $results[] = $row;
  145. }
  146. return $results;
  147. return $func($this->result);
  148. }
  149. /**
  150. * Assert that the current query has been executed.
  151. */
  152. function assert_execution() {
  153. $this->executed || $this->execute();
  154. }
  155. /**
  156. * Set database server login data.
  157. *
  158. * @param string $host The database server to connect with.
  159. * @param string $username The username to login with on the database server.
  160. * @param string $password The password to login with on the database server.
  161. * @param string $dbname The name of the database to select after connecting to the server.
  162. */
  163. static function set_login_data($host, $username, $password, $dbname) {
  164. // Close any existing connection
  165. if( self::$link ) {
  166. mysql_close(self::$link);
  167. self::$link = null;
  168. }
  169. self::$login_data = array_merge(self::$login_data,
  170. compact('host', 'username', 'password', 'dbname'));
  171. }
  172. /**
  173. * Assert that the database server config has been set.
  174. */
  175. static function assert_login_data_exist() {
  176. if( !isset(self::$login_data['host']) )
  177. return self::error('No SQL host specified.');
  178. if( !isset(self::$login_data['username']) )
  179. return self::error('No SQL username specified.');
  180. if( !isset(self::$login_data['password']) )
  181. return self::error('No SQL password specified.');
  182. if( !isset(self::$login_data['host']) )
  183. return self::error('No SQL host specified.');
  184. }
  185. /**
  186. * Assert that the MySQL connection is opened.
  187. *
  188. * @uses mysql_connect,mysql_select_db
  189. */
  190. static function assert_connection() {
  191. // Return if the connection has already been opened
  192. if( self::$link )
  193. return;
  194. self::assert_login_data_exist();
  195. // Connect to the database
  196. $c = self::$login_data;
  197. $link = @mysql_connect($c['host'], $c['username'], $c['password']);
  198. if( $link === false )
  199. return self::mysql_error();
  200. self::$link = $link;
  201. // Select the correct database
  202. if( !@mysql_select_db($c['dbname'], $link) )
  203. return self::mysql_error();
  204. }
  205. /**
  206. * Echo the latest MySQL error.
  207. */
  208. static function mysql_error() {
  209. self::error('MySQL error %d: %s.', mysql_errno(), mysql_error());
  210. }
  211. /**
  212. * Extention of {@link pQuery::error}, returning FALSE (useful in result loops).
  213. * Also, the current query is printed in DEBUG mode.
  214. *
  215. * @returns bool FALSE
  216. */
  217. static function error() {
  218. parent::error('MySQL error %d: %s.', mysql_errno(), mysql_error());
  219. if( DEBUG )
  220. echo $this->query;
  221. return false;
  222. }
  223. /**
  224. * Escape a string for safe use in a query.
  225. *
  226. * @param string $value The string to escape.
  227. * @uses mysql_real_escape_string
  228. */
  229. static function escape($value) {
  230. self::assert_connection();
  231. return mysql_real_escape_string($value, self::$link);
  232. }
  233. }
  234. /**
  235. * Shortcut constructor for {@link pQuerySql}.
  236. *
  237. * @param string $query A MySQL query to evaluate.
  238. * @returns pQuerySql A new SQL query instance.
  239. */
  240. function _sql($query /* [ , $arg1, ... ] */) {
  241. $args = func_get_args();
  242. $query = array_shift($args);
  243. array_unshift($args, 'sql', $query);
  244. return call_user_func_array('pQuery::create', $args);
  245. }
  246. pQuery::extend('pQuerySql', 'sql');
  247. ?>