pquery.sql.php 7.3 KB

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