pquery.sql.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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. // Parse arguments as variables. Arrays and
  59. // Replace variable indices by names equal to their indices
  60. $variables = array();
  61. foreach( $args as $i => $argument ) {
  62. if( is_array($argument) )
  63. $variables = array_merge($variables, $argument);
  64. else
  65. $variables[$i] = $argument;
  66. }
  67. // Replace variables by their escaped values
  68. $this->set($variables);
  69. }
  70. /**
  71. * Replace a set of variables with their (optionally escaped)
  72. * values in the current query.
  73. *
  74. * @param array $variables The variables to replace.
  75. * @param bool $escape Whether to escape the variable values.
  76. * @returns pQuerySql The current query object.
  77. */
  78. function replace_variables($variables, $escape) {
  79. $patterns = array_map('pQuerySql::variable_pattern', array_keys($variables));
  80. $escape && $variables = array_map('pQuerySql::escape', $variables);
  81. $this->variable = preg_replace($patterns, $variables, $this->variable);
  82. $this->executed = false;
  83. return $this;
  84. }
  85. /**
  86. * Replace a set of variables with their escaped values in the current query.
  87. *
  88. * @param array $variables The variables to replace.
  89. * @returns pQuerySql The current query object.
  90. */
  91. function set($variables) {
  92. return $this->replace_variables($variables, true);
  93. }
  94. /**
  95. * Replace a set of variables with their non-escaped values in the current query.
  96. *
  97. * @param array $variables The variables to replace.
  98. * @returns pQuerySql The current query object.
  99. */
  100. function set_unescaped($variables) {
  101. return $this->replace_variables($variables, false);
  102. }
  103. /**
  104. * Transform a variable name to a regex to be used as a replacement
  105. * pattern in a query.
  106. *
  107. * @param string $name The variable name to transform.
  108. * @returns string The variable's replacement pattern.
  109. */
  110. static function variable_pattern($name) {
  111. return sprintf(self::VARIABLE_PATTERN, $name);
  112. }
  113. /**
  114. * Execute the current query.
  115. *
  116. * @returns pQuerySql The current query object.
  117. */
  118. function execute() {
  119. self::assert_connection();
  120. $result = mysql_query($this->query, self::$link);
  121. if( !$result )
  122. return self::mysql_error($this->query);
  123. $this->result = $result;
  124. $this->executed = true;
  125. return $this;
  126. }
  127. /**
  128. * Find the number of resulting rows of the current query.
  129. *
  130. * @returns int The number of result rows.
  131. */
  132. function result_count() {
  133. $this->assert_execution();
  134. if( !$this->result )
  135. return 0;
  136. return mysql_num_rows($this->result);
  137. }
  138. /**
  139. * Fetch a row from the current result.
  140. *
  141. * @param string $type The format of the result row.
  142. * @returns mixed The fetched row in the requested format.
  143. */
  144. function fetch($type=self::DEFAULT_FETCH_TYPE) {
  145. $this->assert_execution();
  146. if( !$this->result )
  147. return self::error('No valid result to fetch from.');
  148. $func = 'mysql_fetch_'.$type;
  149. if( !function_exists($func) )
  150. return self::error('Fetch type "%s" is not supported.', $type);
  151. return $func($this->result);
  152. }
  153. /**
  154. * Fetch all rows from the current result.
  155. *
  156. * @param string $type The format of the result rows.
  157. * @returns array The result set.
  158. */
  159. function fetch_all($type=self::DEFAULT_FETCH_TYPE) {
  160. $results = array();
  161. while( ($row = $this->fetch($type)) !== false ) {
  162. $results[] = $row;
  163. }
  164. return $results;
  165. return $func($this->result);
  166. }
  167. /**
  168. * Assert that the current query has been executed.
  169. */
  170. function assert_execution() {
  171. $this->executed || $this->execute();
  172. }
  173. /**
  174. * Set database server login data.
  175. *
  176. * @param string $host The database server to connect with.
  177. * @param string $username The username to login with on the database server.
  178. * @param string $password The password to login with on the database server.
  179. * @param string $dbname The name of the database to select after connecting to the server.
  180. */
  181. static function set_login_data($host, $username, $password, $dbname) {
  182. // Close any existing connection
  183. self::disconnect();
  184. self::$login_data = array_merge(self::$login_data,
  185. compact('host', 'username', 'password', 'dbname'));
  186. }
  187. /**
  188. * Assert that the database server config has been set.
  189. */
  190. static function assert_login_data_exist() {
  191. if( !isset(self::$login_data['host']) )
  192. return self::error('No MySQL database server host is specified.');
  193. if( !isset(self::$login_data['username']) )
  194. return self::error('No username is specified for the MySQL server.');
  195. if( !isset(self::$login_data['password']) )
  196. return self::error('No password is specified for the MySQL server.');
  197. if( !isset(self::$login_data['host']) )
  198. return self::error('No MySQL database name is specified.');
  199. }
  200. /**
  201. * Assert that the MySQL connection is opened.
  202. *
  203. * @uses mysql_connect,mysql_select_db
  204. */
  205. static function assert_connection() {
  206. // Return if the connection has already been opened
  207. if( self::$link )
  208. return;
  209. self::assert_login_data_exist();
  210. // Connect to the database
  211. $c = self::$login_data;
  212. $link = @mysql_connect($c['host'], $c['username'], $c['password']);
  213. if( $link === false )
  214. return self::mysql_error();
  215. self::$link = $link;
  216. // Select the correct database
  217. if( !@mysql_select_db($c['dbname'], $link) )
  218. return self::mysql_error();
  219. }
  220. /**
  221. * Close the current connection, if any.
  222. *
  223. * @uses mysql_close
  224. */
  225. static function disconnect() {
  226. // Return if the connection has already been closed
  227. if( !self::$link )
  228. return;
  229. mysql_close(self::$link);
  230. self::$link = null;
  231. }
  232. /**
  233. * Echo the latest MySQL error.
  234. * If a query is specified and debug mode is on, add the query to the error message.
  235. *
  236. * @param string $query The query that was executed, if any.
  237. */
  238. static function mysql_error($query='') {
  239. $error = sprintf('MySQL error %d: %s.', mysql_errno(), mysql_error());
  240. PQUERY_DEBUG && $error .= "\nQuery: ".$this->query;
  241. self::error($error);
  242. }
  243. /**
  244. * Extention of {@link pQuery::error}, returning FALSE (useful in result loops).
  245. * Also, the current query is printed in debug mode.
  246. *
  247. * @param string $error The error message
  248. * @returns bool FALSE
  249. */
  250. static function error($error /* [ , $arg1 [ , ... ] ] */) {
  251. $args = func_get_args();
  252. call_user_func_array('pQuery::error', $args);
  253. //parent::error('SQL error %d: %s.', mysql_errno(), mysql_error());
  254. return false;
  255. }
  256. /**
  257. * Escape a string for safe use in a query.
  258. *
  259. * @param string $value The string to escape.
  260. * @uses mysql_real_escape_string
  261. */
  262. static function escape($value) {
  263. self::assert_connection();
  264. return mysql_real_escape_string($value, self::$link);
  265. }
  266. }
  267. /**
  268. * Shortcut constructor for {@link pQuerySql}.
  269. *
  270. * @param string $query A MySQL query to evaluate.
  271. * @returns pQuerySql A new SQL query instance.
  272. */
  273. function _sql($query /* [ , $arg1, ... ] */) {
  274. $args = func_get_args();
  275. $query = array_shift($args);
  276. array_unshift($args, 'sql', $query);
  277. return call_user_func_array('pQuery::create', $args);
  278. }
  279. pQuery::extend('pQuerySql', 'sql');
  280. ?>