pquery.sql.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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. * Find the number of resulting rows of the current query.
  122. *
  123. * @returns int The number of rows.
  124. */
  125. function result_count() {
  126. $this->assert_execution();
  127. if( !$this->result )
  128. return 0;
  129. return mysql_num_rows($this->result);
  130. }
  131. /**
  132. * Fetch a row from the current result.
  133. *
  134. * @param string $type The format of the result row.
  135. * @returns mixed The fetched row in the requested format.
  136. */
  137. function fetch($type=self::DEFAULT_FETCH_TYPE) {
  138. $this->assert_execution();
  139. if( !$this->result )
  140. return self::error('No valid result to fetch from.');
  141. $func = 'mysql_fetch_'.$type;
  142. if( !function_exists($func) )
  143. return self::error('Fetch type "%s" is not supported.', $type);
  144. return $func($this->result);
  145. }
  146. /**
  147. * Fetch all rows from the current result.
  148. *
  149. * @param string $type The format of the result rows.
  150. * @returns array The result set.
  151. */
  152. function fetch_all($type=self::DEFAULT_FETCH_TYPE) {
  153. $results = array();
  154. while( ($row = $this->fetch($type)) !== false ) {
  155. $results[] = $row;
  156. }
  157. return $results;
  158. return $func($this->result);
  159. }
  160. /**
  161. * Assert that the current query has been executed.
  162. */
  163. function assert_execution() {
  164. $this->executed || $this->execute();
  165. }
  166. /**
  167. * Set database server login data.
  168. *
  169. * @param string $host The database server to connect with.
  170. * @param string $username The username to login with on the database server.
  171. * @param string $password The password to login with on the database server.
  172. * @param string $dbname The name of the database to select after connecting to the server.
  173. */
  174. static function set_login_data($host, $username, $password, $dbname) {
  175. // Close any existing connection
  176. if( self::$link ) {
  177. mysql_close(self::$link);
  178. self::$link = null;
  179. }
  180. self::$login_data = array_merge(self::$login_data,
  181. compact('host', 'username', 'password', 'dbname'));
  182. }
  183. /**
  184. * Assert that the database server config has been set.
  185. */
  186. static function assert_login_data_exist() {
  187. if( !isset(self::$login_data['host']) )
  188. return self::error('No SQL host specified.');
  189. if( !isset(self::$login_data['username']) )
  190. return self::error('No SQL username specified.');
  191. if( !isset(self::$login_data['password']) )
  192. return self::error('No SQL password specified.');
  193. if( !isset(self::$login_data['host']) )
  194. return self::error('No SQL host specified.');
  195. }
  196. /**
  197. * Assert that the MySQL connection is opened.
  198. *
  199. * @uses mysql_connect,mysql_select_db
  200. */
  201. static function assert_connection() {
  202. // Return if the connection has already been opened
  203. if( self::$link )
  204. return;
  205. self::assert_login_data_exist();
  206. // Connect to the database
  207. $c = self::$login_data;
  208. $link = @mysql_connect($c['host'], $c['username'], $c['password']);
  209. if( $link === false )
  210. return self::mysql_error();
  211. self::$link = $link;
  212. // Select the correct database
  213. if( !@mysql_select_db($c['dbname'], $link) )
  214. return self::mysql_error();
  215. }
  216. /**
  217. * Echo the latest MySQL error.
  218. */
  219. static function mysql_error() {
  220. self::error('MySQL error %d: %s.', mysql_errno(), mysql_error());
  221. }
  222. /**
  223. * Extention of {@link pQuery::error}, returning FALSE (useful in result loops).
  224. * Also, the current query is printed in DEBUG mode.
  225. *
  226. * @returns bool FALSE
  227. */
  228. static function error() {
  229. parent::error('MySQL error %d: %s.', mysql_errno(), mysql_error());
  230. if( DEBUG )
  231. echo $this->query;
  232. return false;
  233. }
  234. /**
  235. * Escape a string for safe use in a query.
  236. *
  237. * @param string $value The string to escape.
  238. * @uses mysql_real_escape_string
  239. */
  240. static function escape($value) {
  241. self::assert_connection();
  242. return mysql_real_escape_string($value, self::$link);
  243. }
  244. }
  245. /**
  246. * Shortcut constructor for {@link pQuerySql}.
  247. *
  248. * @param string $query A MySQL query to evaluate.
  249. * @returns pQuerySql A new SQL query instance.
  250. */
  251. function _sql($query /* [ , $arg1, ... ] */) {
  252. $args = func_get_args();
  253. $query = array_shift($args);
  254. array_unshift($args, 'sql', $query);
  255. return call_user_func_array('pQuery::create', $args);
  256. }
  257. pQuery::extend('pQuerySql', 'sql');
  258. ?>