PageRenderTime 43ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 1ms

/core/engines/mysql.engine.php

http://snowcms.googlecode.com/
PHP | 555 lines | 369 code | 100 blank | 86 comment | 50 complexity | 2f4c5de3533c397f14f84c5da453a0c5 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. <?php
  2. ////////////////////////////////////////////////////////////////////////////
  3. // SnowCMS v2.0 //
  4. // By the SnowCMS Team //
  5. // www.snowcms.com //
  6. // Released under the Microsoft Reciprocal License //
  7. // www.opensource.org/licenses/ms-rl.html //
  8. ////////////////////////////////////////////////////////////////////////////
  9. // //
  10. // SnowCMS originally pawned by soren121 started in early 2008 //
  11. // //
  12. ////////////////////////////////////////////////////////////////////////////
  13. // //
  14. // SnowCMS v2.0 began in November 2009 //
  15. // //
  16. ////////////////////////////////////////////////////////////////////////////
  17. // File version: SnowCMS 2.0 //
  18. ////////////////////////////////////////////////////////////////////////////
  19. if(!defined('INSNOW'))
  20. {
  21. die('Nice try...');
  22. }
  23. class MySQL extends Database
  24. {
  25. public function connect()
  26. {
  27. // Persistent connection or not?
  28. if(!defined('dbpersist') || !dbpersist)
  29. {
  30. $this->con = @mysql_connect(dbhost, dbuser, dbpass);
  31. }
  32. else
  33. {
  34. $this->con = @mysql_pconnect(dbhost, dbuser, dbpass);
  35. }
  36. // Fail to connect?
  37. if(empty($this->con))
  38. {
  39. // Show the message.
  40. $this->log_error(1, true);
  41. return false;
  42. }
  43. // Select the database now ;)
  44. $select_db = @mysql_select_db(dbname, $this->con);
  45. // Failed to select the database..? That isn't good!
  46. if(empty($select_db))
  47. {
  48. $this->log_error(2, true);
  49. }
  50. // Sweet, everything seems to be in order so far, set a couple other things others
  51. // may need to use at a later time.
  52. $this->prefix = tblprefix;
  53. $this->type = 'MySQL';
  54. $this->case_sensitive = false;
  55. $this->drop_if_exists = true;
  56. $this->if_not_exists = true;
  57. $this->extended_inserts = true;
  58. // Alright, we are done here.
  59. return true;
  60. }
  61. public function close()
  62. {
  63. return @mysql_close($this->con);
  64. }
  65. public function errno()
  66. {
  67. return @mysql_errno($this->con);
  68. }
  69. public function error()
  70. {
  71. return @mysql_error($this->con);
  72. }
  73. public function escape($str, $htmlspecialchars = false)
  74. {
  75. global $func;
  76. return @mysql_real_escape_string(!empty($htmlspecialchars) ? $func['htmlspecialchars']($str) : $str, $this->con);
  77. }
  78. public function unescape($str, $htmlspecialchars_decode = false)
  79. {
  80. global $func;
  81. return !empty($htmlspecialchars_decode) ? $func['htmlspecialchars_decode'](stripslashes($str)) : stripslashes($str);
  82. }
  83. public function version()
  84. {
  85. if(empty($this->con))
  86. {
  87. return false;
  88. }
  89. // The VERSION() function will give us what we need :-)
  90. $result = $this->query('
  91. SELECT VERSION()');
  92. // Fetch it and return it!
  93. list($version) = $result->fetch_row();
  94. return $version;
  95. }
  96. public function tables()
  97. {
  98. if(empty($this->con))
  99. {
  100. return false;
  101. }
  102. // Get a listing of all the tables, simpler than SQLite...
  103. $result = $db->query('
  104. SHOW TABLES', array(), null, 'show_tables');
  105. // Load'em up, otherwise, what's the point, right..?
  106. $tables = array();
  107. while($row = $result->fetch_row())
  108. {
  109. $tables[] = $row[0];
  110. }
  111. return $tables;
  112. }
  113. public function columns($table)
  114. {
  115. // Check to see if the table exists, if it does not, we cannot get the columns :P
  116. if(empty($this->con) || !in_array($table, $this->tables()))
  117. {
  118. return false;
  119. }
  120. // Simple query, really.
  121. $result = $db->query('
  122. SHOW COLUMNS IN {raw:table}',
  123. array(
  124. 'table' => $table,
  125. ), null, 'show_columns');
  126. // Now get ready to load'em up.
  127. $columns = array();
  128. while($row = $result->fetch_row())
  129. {
  130. $columns[] = $row[0];
  131. }
  132. return $columns;
  133. }
  134. public function query($db_query, $db_vars = array(), $hook_name = null, $db_compat = null, $file = null, $line = 0)
  135. {
  136. // We can't make a query if we aren't connected.
  137. if(empty($this->con))
  138. {
  139. return false;
  140. }
  141. // Something for all queries before they are parsed.
  142. $return = null;
  143. api()->run_hooks('pre_parse_query', array(&$db_query, &$db_vars, &$hook_name, &$db_compat, &$file, &$line, &$return));
  144. if($return !== null)
  145. {
  146. return $return;
  147. }
  148. // Just incase, for some odd reason :P
  149. if(!empty($hook_name))
  150. {
  151. $return = null;
  152. api()->run_hooks($hook_name, array(&$db_query, &$db_vars, &$hook_name, &$db_compat, &$file, &$line, &$return));
  153. if($return !== null)
  154. {
  155. return $return;
  156. }
  157. }
  158. // debug set?
  159. if(isset($db_vars['debug']))
  160. {
  161. $prev_debug = $this->debug;
  162. $this->debug = !empty($db_vars['debug']);
  163. unset($db_vars['debug']);
  164. }
  165. /*
  166. In other databases such as SQLite, PostgreSQL, SQL Server, etc. anything that isn't
  167. MySQL at this time would do any query fixing to make it parse right upon execution.
  168. */
  169. // Let's use debug_backtrace() to find where this was called and what not ;)
  170. // Only if file and line aren't set already ;)
  171. if(empty($file) || empty($line))
  172. {
  173. $backtrace = debug_backtrace();
  174. $file = $backtrace[0]['file'];
  175. $line = (int)$backtrace[0]['line'];
  176. }
  177. // Replace {db->prefix} and {db_prefix} with $this->prefix... :P
  178. $db_query = strtr($db_query, array('{db->prefix}' => $this->prefix, '{db_prefix}' => $this->prefix));
  179. // Any possible variables that may need replacing? (Don't do this if it is an insert, or things could get ugly,
  180. // or don't do it if you said no_parse ;-))
  181. if(strpos($db_query, '{') !== false && ($db_compat != 'insert' || $db_compat == 'no_parse'))
  182. {
  183. // Find all the variables.
  184. preg_match_all('~{[\w-]+:\w+}~', $db_query, $matches);
  185. if(count($matches[0]))
  186. {
  187. // Holds all our soon-to-be replaced variables.
  188. $replacements = array();
  189. // Holds onto any undefined variables, you never know ;)
  190. $undefined = array();
  191. // Let's figure out which variables are defined.
  192. $defined = array();
  193. foreach($db_vars as $var_name => $value)
  194. {
  195. $defined[] = $var_name;
  196. }
  197. // No need to parse the same variables multiple times, is there?
  198. $matches[0] = array_unique($matches[0]);
  199. foreach($matches[0] as $variable)
  200. {
  201. list($datatype, $variable_name) = explode(':', substr($variable, 1, strlen($variable) - 2));
  202. // Let's just be safe, shall we?
  203. $datatype = trim($datatype);
  204. $variable_name = trim($variable_name);
  205. // Has it been defined or not?
  206. if(!in_array($variable_name, $defined))
  207. {
  208. $undefined[] = $variable_name;
  209. continue;
  210. }
  211. // Sanitize that value to how it should be!!!
  212. $replacements[$variable] = $this->var_sanitize($variable_name, $datatype, $db_vars[$variable_name], $file, $line);
  213. }
  214. // Did we get any undefined variables? :/
  215. if(count($undefined) > 0)
  216. {
  217. $this->log_error('Undefined database variables <em>'. implode('</em>, <em>', $undefined). '</em>', true, $file, $line);
  218. }
  219. // Maybe replace the variables in the query?
  220. if(count($replacements) > 0)
  221. {
  222. $db_query = strtr($db_query, $replacements);
  223. }
  224. }
  225. }
  226. // For every query...
  227. $return = null;
  228. api()->run_hooks('pre_query_exec', array(&$db_query, &$db_vars, &$db_compat, &$hook_name, &$return));
  229. if(!empty($return))
  230. {
  231. return $return;
  232. }
  233. // Woo!!! QUERY THAT DATABASE!
  234. $query_start = microtime(true);
  235. $query_result = mysql_query(trim($db_query), $this->con);
  236. $query_took = round(microtime(true) - $query_start, 5);
  237. // That is one more query!
  238. $this->num_queries++;
  239. // Let's not call on it multiple times, mmk?
  240. $mysql_errno = $this->errno();
  241. $mysql_error = $this->error();
  242. // Debug this query, perhaps?
  243. if(!empty($this->debug))
  244. {
  245. $this->debug_text .= "Query:\r\n$db_query\r\nFile: $file\r\nLine: $line\r\nExecuted in $query_took seconds.\r\nError: ". (empty($query_result) ? '['. $mysql_errno. '] '. $mysql_error : 'None'). "\r\n\r\n";
  246. $this->debug = isset($prev_debug) ? $prev_debug : $this->debug;
  247. }
  248. // An error occur?
  249. if(empty($query_result))
  250. {
  251. $this->log_error('['. $mysql_errno. '] '. $mysql_error, true, $file, $line);
  252. }
  253. // Put it in a MySQLResult Object ;)
  254. $result = new $this->result_class($query_result, mysql_affected_rows($this->con), $db_compat == 'insert' ? mysql_insert_id($this->con) : 0, $mysql_errno, $mysql_error, $this->num_queries - 1);
  255. api()->run_hooks('post_query_exec', array(&$result, $db_query, $query_result, $this->result_class, $db_compat, $hook_name, $query_took, $mysql_errno, $mysql_error));
  256. return $result;
  257. }
  258. protected function var_sanitize($var_name, $datatype, $value, $file, $line)
  259. {
  260. $datatype = strtolower($datatype);
  261. // Is it a string? It could have a length :)
  262. if(substr($datatype, 0, 6) == 'string' && strpos($datatype, '-') !== false)
  263. {
  264. list(, $length) = explode('-', $datatype);
  265. $datatype = 'string';
  266. $value = substr($value, 0, (int)$length);
  267. }
  268. $datatypes = array(
  269. 'float' => 'sanitize_float',
  270. 'float_array' => 'sanitize_float_array',
  271. 'array_float' => 'sanitize_float_array',
  272. 'identifier' => 'sanitize_identifier',
  273. 'identifier_array' => 'sanitize_identifier_array',
  274. 'array_identifier' => 'sanitize_identifier_array',
  275. 'int' => 'sanitize_int',
  276. 'int_array' => 'sanitize_int_array',
  277. 'array_int' => 'sanitize_int_array',
  278. 'raw' => 'sanitize_raw',
  279. 'string' => 'sanitize_string',
  280. 'string_array' => 'sanitize_string_array',
  281. 'array_string' => 'sanitize_string_array',
  282. 'text' => 'sanitize_string',
  283. 'text_array' => 'sanitize_string_array',
  284. 'array_text' => 'sanitize_string_array',
  285. );
  286. api()->run_hooks('database_types', array(&$datatypes));
  287. // Is the datatype defined?
  288. if(!isset($datatypes[$datatype]))
  289. {
  290. $this->log_error('Undefined data type <string>'. strtoupper($datatype). '</strong>.', true, $file, $line);
  291. }
  292. // Return the sanitized value...
  293. return is_callable(array($this, $datatypes[$datatype])) ? $this->$datatypes[$datatype]($var_name, $value, $file, $line) : $datatypes[$datatype]($var_name, $value, $file, $line);
  294. }
  295. protected function sanitize_float($var_name, $value, $file, $line)
  296. {
  297. // Make sure it is of the right type :)
  298. if((string)$value !== (string)(float)$value)
  299. {
  300. $this->log_error('Wrong data type, float expected ('. $var_name. ')', true, $file, $line);
  301. }
  302. return (string)(float)$value;
  303. }
  304. protected function sanitize_float_array($var_name, $value, $file, $line)
  305. {
  306. // Not an array? Well, it can't be an array of floats then can it?
  307. if(!is_array($value))
  308. {
  309. $this->log_error('Wrong data type, array expected ('. $var_name. ')', true, $file, $line);
  310. }
  311. $new_value = array();
  312. if(count($value))
  313. {
  314. foreach($value as $v)
  315. {
  316. $new_value[] = $this->sanitize_float($var_name, $v, $file, $line);
  317. }
  318. }
  319. return implode(', ', $new_value);
  320. }
  321. protected function sanitize_identifier($var_name, $value, $file, $line)
  322. {
  323. // No sanitization here, actually.
  324. return '`'. $value. '`';
  325. }
  326. protected function sanitize_identifier_array($var_name, $value, $file, $line)
  327. {
  328. if(!is_array($value))
  329. {
  330. $this->log_error('Wrong data type, array expected ('. $var_name. ')', true, $file, $line);
  331. }
  332. $new_value = array();
  333. if(count($value))
  334. {
  335. foreach($value as $v)
  336. {
  337. $new_value[] = $this->sanitize_identifier($var_name, $v, $file, $line);
  338. }
  339. }
  340. return implode(', ', $new_value);
  341. }
  342. protected function sanitize_int($var_name, $value, $file, $line)
  343. {
  344. // Mmmm, inty!
  345. if((string)$value !== (string)(int)$value)
  346. {
  347. $this->log_error('Wrong data type, integer expected ('. $var_name. ')', true, $file, $line);
  348. }
  349. return (string)(int)$value;
  350. }
  351. protected function sanitize_int_array($var_name, $value, $file, $line)
  352. {
  353. if(!is_array($value))
  354. {
  355. $this->log_error('Wrong data type, array expected ('. $var_name. ')', true, $file, $line);
  356. }
  357. $new_value = array();
  358. if(count($value))
  359. {
  360. foreach($value as $v)
  361. {
  362. $new_value[] = $this->sanitize_int($var_name, $v, $file, $line);
  363. }
  364. }
  365. return implode(', ', $new_value);
  366. }
  367. protected function sanitize_string($var_name, $value, $file, $line)
  368. {
  369. // No need to see if it is a string P:
  370. return '\''. $this->escape($value). '\'';
  371. }
  372. protected function sanitize_string_array($var_name, $value, $file, $line)
  373. {
  374. if(!is_array($value))
  375. {
  376. $this->log_error('Wrong data type, array expected ('. $var_name. ')', true, $file, $line);
  377. }
  378. $new_value = array();
  379. if(count($value))
  380. {
  381. foreach($value as $v)
  382. {
  383. $new_value[] = $this->sanitize_string($var_name, $v, $file, $line);
  384. }
  385. }
  386. return implode(', ', $new_value);
  387. }
  388. public function insert($type, $tbl_name, $columns, $data, $keys = array(), $hook_name = null)
  389. {
  390. if(empty($this->con))
  391. {
  392. return false;
  393. }
  394. // Something for all inserts.
  395. api()->run_hooks('pre_insert_exec', array(&$type, &$tbl_name, &$columns, &$data, &$keys, &$hook_name));
  396. if(!empty($hook_name))
  397. {
  398. api()->run_hooks($hook_name, array(&$type, &$tbl_name, &$columns, &$data, &$keys, &$hook_name));
  399. }
  400. // Let's get where you called us from!
  401. $backtrace = debug_backtrace();
  402. $file = realpath($backtrace[0]['file']);
  403. $line = (int)$backtrace[0]['line'];
  404. unset($backtrace);
  405. $type = strtolower($type);
  406. // We only support insert, ignore and replace.
  407. if(!in_array($type, array('insert', 'ignore', 'replace')))
  408. {
  409. $this->log_error('Unknown insert type '. $type, true, $file, $line);
  410. }
  411. // Replace {db->prefix} and {db_prefix} with $this->prefix
  412. $tbl_name = strtr($tbl_name, array('{db->prefix}' => $this->prefix, '{db_prefix}' => $this->prefix));
  413. // Just an array, and not an array inside an array? We'll fix that...
  414. if(!isset($data[0]) || !is_array($data[0]))
  415. {
  416. $data = array($data);
  417. }
  418. // The number of columns :)
  419. $num_columns = count($columns);
  420. // Now get the column names, quite useful you know :)
  421. $column_names = array_keys($columns);
  422. // Now we can get all the rows ready :)
  423. $rows = array();
  424. foreach($data as $row_index => $row)
  425. {
  426. // Not enough data?
  427. if($num_columns != count($row))
  428. {
  429. $this->log_error('Number of columns doesn\'t match the number of supplied columns in row //'. ($row_index + 1), true, $file, $line);
  430. }
  431. // Save the values to an array, all sanitized and what not, of course!
  432. $values = array();
  433. foreach($row as $index => $value)
  434. {
  435. $values[] = $this->var_sanitize($column_names[$index], $columns[$column_names[$index]], $value, $file, $line);
  436. }
  437. // Add those values to our rows now :)
  438. $rows[] = '('. implode(', ', $values). ')';
  439. }
  440. $inserts = array(
  441. 'insert' => 'INSERT',
  442. 'ignore' => 'INSERT IGNORE',
  443. 'replace' => 'REPLACE',
  444. );
  445. // Construct the query, MySQL suports extended inserts! Hip hip! HURRAY!
  446. $db_query = $inserts[$type]. ' INTO `'. $tbl_name. '` (`'. implode('`, `', $column_names). '`) VALUES'. implode(', ', $rows);
  447. // Let query handle it XD! (passes insert in db compat to let you know
  448. // if you don't have to do anything at all, which you shouldn't!!!
  449. return $this->query($db_query, array(), null, 'insert');
  450. }
  451. }
  452. $db_class = 'MySQL';
  453. ?>