Verzeichnisstruktur phpBB-3.1.0
- Veröffentlicht
- 27.10.2014
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
driver.php
0001 <?php
0002 /**
0003 *
0004 * This file is part of the phpBB Forum Software package.
0005 *
0006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007 * @license GNU General Public License, version 2 (GPL-2.0)
0008 *
0009 * For full copyright and license information, please see
0010 * the docs/CREDITS.txt file.
0011 *
0012 */
0013
0014 namespace phpbb\db\driver;
0015
0016 /**
0017 * Database Abstraction Layer
0018 */
0019 abstract class driver implements driver_interface
0020 {
0021 var $db_connect_id;
0022 var $query_result;
0023 var $return_on_error = false;
0024 var $transaction = false;
0025 var $sql_time = 0;
0026 var $num_queries = array();
0027 var $open_queries = array();
0028
0029 var $curtime = 0;
0030 var $query_hold = '';
0031 var $html_hold = '';
0032 var $sql_report = '';
0033
0034 var $persistency = false;
0035 var $user = '';
0036 var $server = '';
0037 var $dbname = '';
0038
0039 // Set to true if error triggered
0040 var $sql_error_triggered = false;
0041
0042 // Holding the last sql query on sql error
0043 var $sql_error_sql = '';
0044 // Holding the error information - only populated if sql_error_triggered is set
0045 var $sql_error_returned = array();
0046
0047 // Holding transaction count
0048 var $transactions = 0;
0049
0050 // Supports multi inserts?
0051 var $multi_insert = false;
0052
0053 /**
0054 * Current sql layer
0055 */
0056 var $sql_layer = '';
0057
0058 /**
0059 * Wildcards for matching any (%) or exactly one (_) character within LIKE expressions
0060 */
0061 var $any_char;
0062 var $one_char;
0063
0064 /**
0065 * Exact version of the DBAL, directly queried
0066 */
0067 var $sql_server_version = false;
0068
0069 /**
0070 * Constructor
0071 */
0072 function __construct()
0073 {
0074 $this->num_queries = array(
0075 'cached' => 0,
0076 'normal' => 0,
0077 'total' => 0,
0078 );
0079
0080 // Fill default sql layer based on the class being called.
0081 // This can be changed by the specified layer itself later if needed.
0082 $this->sql_layer = substr(get_class($this), strlen('phpbb\db\driver\\'));
0083
0084 // Do not change this please! This variable is used to easy the use of it - and is hardcoded.
0085 $this->any_char = chr(0) . '%';
0086 $this->one_char = chr(0) . '_';
0087 }
0088
0089 /**
0090 * {@inheritdoc}
0091 */
0092 public function get_sql_layer()
0093 {
0094 return $this->sql_layer;
0095 }
0096
0097 /**
0098 * {@inheritdoc}
0099 */
0100 public function get_db_name()
0101 {
0102 return $this->dbname;
0103 }
0104
0105 /**
0106 * {@inheritdoc}
0107 */
0108 public function get_any_char()
0109 {
0110 return $this->any_char;
0111 }
0112
0113 /**
0114 * {@inheritdoc}
0115 */
0116 public function get_one_char()
0117 {
0118 return $this->one_char;
0119 }
0120
0121 /**
0122 * {@inheritdoc}
0123 */
0124 public function get_db_connect_id()
0125 {
0126 return $this->db_connect_id;
0127 }
0128
0129 /**
0130 * {@inheritdoc}
0131 */
0132 public function get_sql_error_triggered()
0133 {
0134 return $this->sql_error_triggered;
0135 }
0136
0137 /**
0138 * {@inheritdoc}
0139 */
0140 public function get_sql_error_sql()
0141 {
0142 return $this->sql_error_sql;
0143 }
0144
0145 /**
0146 * {@inheritdoc}
0147 */
0148 public function get_transaction()
0149 {
0150 return $this->transaction;
0151 }
0152
0153 /**
0154 * {@inheritdoc}
0155 */
0156 public function get_sql_time()
0157 {
0158 return $this->sql_time;
0159 }
0160
0161 /**
0162 * {@inheritdoc}
0163 */
0164 public function get_sql_error_returned()
0165 {
0166 return $this->sql_error_returned;
0167 }
0168
0169 /**
0170 * {@inheritdoc}
0171 */
0172 public function get_multi_insert()
0173 {
0174 return $this->multi_insert;
0175 }
0176
0177 /**
0178 * {@inheritdoc}
0179 */
0180 public function set_multi_insert($multi_insert)
0181 {
0182 $this->multi_insert = $multi_insert;
0183 }
0184
0185 /**
0186 * {@inheritDoc}
0187 */
0188 function sql_return_on_error($fail = false)
0189 {
0190 $this->sql_error_triggered = false;
0191 $this->sql_error_sql = '';
0192
0193 $this->return_on_error = $fail;
0194 }
0195
0196 /**
0197 * {@inheritDoc}
0198 */
0199 function sql_num_queries($cached = false)
0200 {
0201 return ($cached) ? $this->num_queries['cached'] : $this->num_queries['normal'];
0202 }
0203
0204 /**
0205 * {@inheritDoc}
0206 */
0207 function sql_add_num_queries($cached = false)
0208 {
0209 $this->num_queries['cached'] += ($cached !== false) ? 1 : 0;
0210 $this->num_queries['normal'] += ($cached !== false) ? 0 : 1;
0211 $this->num_queries['total'] += 1;
0212 }
0213
0214 /**
0215 * {@inheritDoc}
0216 */
0217 function sql_close()
0218 {
0219 if (!$this->db_connect_id)
0220 {
0221 return false;
0222 }
0223
0224 if ($this->transaction)
0225 {
0226 do
0227 {
0228 $this->sql_transaction('commit');
0229 }
0230 while ($this->transaction);
0231 }
0232
0233 foreach ($this->open_queries as $query_id)
0234 {
0235 $this->sql_freeresult($query_id);
0236 }
0237
0238 // Connection closed correctly. Set db_connect_id to false to prevent errors
0239 if ($result = $this->_sql_close())
0240 {
0241 $this->db_connect_id = false;
0242 }
0243
0244 return $result;
0245 }
0246
0247 /**
0248 * {@inheritDoc}
0249 */
0250 function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
0251 {
0252 if (empty($query))
0253 {
0254 return false;
0255 }
0256
0257 // Never use a negative total or offset
0258 $total = ($total < 0) ? 0 : $total;
0259 $offset = ($offset < 0) ? 0 : $offset;
0260
0261 return $this->_sql_query_limit($query, $total, $offset, $cache_ttl);
0262 }
0263
0264 /**
0265 * {@inheritDoc}
0266 */
0267 function sql_fetchrowset($query_id = false)
0268 {
0269 if ($query_id === false)
0270 {
0271 $query_id = $this->query_result;
0272 }
0273
0274 if ($query_id !== false)
0275 {
0276 $result = array();
0277 while ($row = $this->sql_fetchrow($query_id))
0278 {
0279 $result[] = $row;
0280 }
0281
0282 return $result;
0283 }
0284
0285 return false;
0286 }
0287
0288 /**
0289 * {@inheritDoc}
0290 */
0291 function sql_rowseek($rownum, &$query_id)
0292 {
0293 global $cache;
0294
0295 if ($query_id === false)
0296 {
0297 $query_id = $this->query_result;
0298 }
0299
0300 if ($cache && $cache->sql_exists($query_id))
0301 {
0302 return $cache->sql_rowseek($rownum, $query_id);
0303 }
0304
0305 if ($query_id === false)
0306 {
0307 return false;
0308 }
0309
0310 $this->sql_freeresult($query_id);
0311 $query_id = $this->sql_query($this->last_query_text);
0312
0313 if ($query_id === false)
0314 {
0315 return false;
0316 }
0317
0318 // We do not fetch the row for rownum == 0 because then the next resultset would be the second row
0319 for ($i = 0; $i < $rownum; $i++)
0320 {
0321 if (!$this->sql_fetchrow($query_id))
0322 {
0323 return false;
0324 }
0325 }
0326
0327 return true;
0328 }
0329
0330 /**
0331 * {@inheritDoc}
0332 */
0333 function sql_fetchfield($field, $rownum = false, $query_id = false)
0334 {
0335 global $cache;
0336
0337 if ($query_id === false)
0338 {
0339 $query_id = $this->query_result;
0340 }
0341
0342 if ($query_id !== false)
0343 {
0344 if ($rownum !== false)
0345 {
0346 $this->sql_rowseek($rownum, $query_id);
0347 }
0348
0349 if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
0350 {
0351 return $cache->sql_fetchfield($query_id, $field);
0352 }
0353
0354 $row = $this->sql_fetchrow($query_id);
0355 return (isset($row[$field])) ? $row[$field] : false;
0356 }
0357
0358 return false;
0359 }
0360
0361 /**
0362 * {@inheritDoc}
0363 */
0364 function sql_like_expression($expression)
0365 {
0366 $expression = utf8_str_replace(array('_', '%'), array("\_", "\%"), $expression);
0367 $expression = utf8_str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
0368
0369 return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\'');
0370 }
0371
0372 /**
0373 * {@inheritDoc}
0374 */
0375 function sql_not_like_expression($expression)
0376 {
0377 $expression = utf8_str_replace(array('_', '%'), array("\_", "\%"), $expression);
0378 $expression = utf8_str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
0379
0380 return $this->_sql_not_like_expression('NOT LIKE \'' . $this->sql_escape($expression) . '\'');
0381 }
0382
0383 /**
0384 * {@inheritDoc}
0385 */
0386 public function sql_case($condition, $action_true, $action_false = false)
0387 {
0388 $sql_case = 'CASE WHEN ' . $condition;
0389 $sql_case .= ' THEN ' . $action_true;
0390 $sql_case .= ($action_false !== false) ? ' ELSE ' . $action_false : '';
0391 $sql_case .= ' END';
0392 return $sql_case;
0393 }
0394
0395 /**
0396 * {@inheritDoc}
0397 */
0398 public function sql_concatenate($expr1, $expr2)
0399 {
0400 return $expr1 . ' || ' . $expr2;
0401 }
0402
0403 /**
0404 * {@inheritDoc}
0405 */
0406 function sql_buffer_nested_transactions()
0407 {
0408 return false;
0409 }
0410
0411 /**
0412 * {@inheritDoc}
0413 */
0414 function sql_transaction($status = 'begin')
0415 {
0416 switch ($status)
0417 {
0418 case 'begin':
0419 // If we are within a transaction we will not open another one, but enclose the current one to not loose data (preventing auto commit)
0420 if ($this->transaction)
0421 {
0422 $this->transactions++;
0423 return true;
0424 }
0425
0426 $result = $this->_sql_transaction('begin');
0427
0428 if (!$result)
0429 {
0430 $this->sql_error();
0431 }
0432
0433 $this->transaction = true;
0434 break;
0435
0436 case 'commit':
0437 // If there was a previously opened transaction we do not commit yet...
0438 // but count back the number of inner transactions
0439 if ($this->transaction && $this->transactions)
0440 {
0441 $this->transactions--;
0442 return true;
0443 }
0444
0445 // Check if there is a transaction (no transaction can happen if
0446 // there was an error, with a combined rollback and error returning enabled)
0447 // This implies we have transaction always set for autocommit db's
0448 if (!$this->transaction)
0449 {
0450 return false;
0451 }
0452
0453 $result = $this->_sql_transaction('commit');
0454
0455 if (!$result)
0456 {
0457 $this->sql_error();
0458 }
0459
0460 $this->transaction = false;
0461 $this->transactions = 0;
0462 break;
0463
0464 case 'rollback':
0465 $result = $this->_sql_transaction('rollback');
0466 $this->transaction = false;
0467 $this->transactions = 0;
0468 break;
0469
0470 default:
0471 $result = $this->_sql_transaction($status);
0472 break;
0473 }
0474
0475 return $result;
0476 }
0477
0478 /**
0479 * {@inheritDoc}
0480 */
0481 function sql_build_array($query, $assoc_ary = false)
0482 {
0483 if (!is_array($assoc_ary))
0484 {
0485 return false;
0486 }
0487
0488 $fields = $values = array();
0489
0490 if ($query == 'INSERT' || $query == 'INSERT_SELECT')
0491 {
0492 foreach ($assoc_ary as $key => $var)
0493 {
0494 $fields[] = $key;
0495
0496 if (is_array($var) && is_string($var[0]))
0497 {
0498 // This is used for INSERT_SELECT(s)
0499 $values[] = $var[0];
0500 }
0501 else
0502 {
0503 $values[] = $this->_sql_validate_value($var);
0504 }
0505 }
0506
0507 $query = ($query == 'INSERT') ? ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')' : ' (' . implode(', ', $fields) . ') SELECT ' . implode(', ', $values) . ' ';
0508 }
0509 else if ($query == 'MULTI_INSERT')
0510 {
0511 trigger_error('The MULTI_INSERT query value is no longer supported. Please use sql_multi_insert() instead.', E_USER_ERROR);
0512 }
0513 else if ($query == 'UPDATE' || $query == 'SELECT' || $query == 'DELETE')
0514 {
0515 $values = array();
0516 foreach ($assoc_ary as $key => $var)
0517 {
0518 $values[] = "$key = " . $this->_sql_validate_value($var);
0519 }
0520 $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
0521 }
0522
0523 return $query;
0524 }
0525
0526 /**
0527 * {@inheritDoc}
0528 */
0529 function sql_in_set($field, $array, $negate = false, $allow_empty_set = false)
0530 {
0531 if (!sizeof($array))
0532 {
0533 if (!$allow_empty_set)
0534 {
0535 // Print the backtrace to help identifying the location of the problematic code
0536 $this->sql_error('No values specified for SQL IN comparison');
0537 }
0538 else
0539 {
0540 // NOT IN () actually means everything so use a tautology
0541 if ($negate)
0542 {
0543 return '1=1';
0544 }
0545 // IN () actually means nothing so use a contradiction
0546 else
0547 {
0548 return '1=0';
0549 }
0550 }
0551 }
0552
0553 if (!is_array($array))
0554 {
0555 $array = array($array);
0556 }
0557
0558 if (sizeof($array) == 1)
0559 {
0560 @reset($array);
0561 $var = current($array);
0562
0563 return $field . ($negate ? ' <> ' : ' = ') . $this->_sql_validate_value($var);
0564 }
0565 else
0566 {
0567 return $field . ($negate ? ' NOT IN ' : ' IN ') . '(' . implode(', ', array_map(array($this, '_sql_validate_value'), $array)) . ')';
0568 }
0569 }
0570
0571 /**
0572 * {@inheritDoc}
0573 */
0574 function sql_bit_and($column_name, $bit, $compare = '')
0575 {
0576 if (method_exists($this, '_sql_bit_and'))
0577 {
0578 return $this->_sql_bit_and($column_name, $bit, $compare);
0579 }
0580
0581 return $column_name . ' & ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
0582 }
0583
0584 /**
0585 * {@inheritDoc}
0586 */
0587 function sql_bit_or($column_name, $bit, $compare = '')
0588 {
0589 if (method_exists($this, '_sql_bit_or'))
0590 {
0591 return $this->_sql_bit_or($column_name, $bit, $compare);
0592 }
0593
0594 return $column_name . ' | ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
0595 }
0596
0597 /**
0598 * {@inheritDoc}
0599 */
0600 function cast_expr_to_bigint($expression)
0601 {
0602 return $expression;
0603 }
0604
0605 /**
0606 * {@inheritDoc}
0607 */
0608 function cast_expr_to_string($expression)
0609 {
0610 return $expression;
0611 }
0612
0613 /**
0614 * {@inheritDoc}
0615 */
0616 function sql_lower_text($column_name)
0617 {
0618 return "LOWER($column_name)";
0619 }
0620
0621 /**
0622 * {@inheritDoc}
0623 */
0624 function sql_multi_insert($table, $sql_ary)
0625 {
0626 if (!sizeof($sql_ary))
0627 {
0628 return false;
0629 }
0630
0631 if ($this->multi_insert)
0632 {
0633 $ary = array();
0634 foreach ($sql_ary as $id => $_sql_ary)
0635 {
0636 // If by accident the sql array is only one-dimensional we build a normal insert statement
0637 if (!is_array($_sql_ary))
0638 {
0639 return $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $sql_ary));
0640 }
0641
0642 $values = array();
0643 foreach ($_sql_ary as $key => $var)
0644 {
0645 $values[] = $this->_sql_validate_value($var);
0646 }
0647 $ary[] = '(' . implode(', ', $values) . ')';
0648 }
0649
0650 return $this->sql_query('INSERT INTO ' . $table . ' ' . ' (' . implode(', ', array_keys($sql_ary[0])) . ') VALUES ' . implode(', ', $ary));
0651 }
0652 else
0653 {
0654 foreach ($sql_ary as $ary)
0655 {
0656 if (!is_array($ary))
0657 {
0658 return false;
0659 }
0660
0661 $result = $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $ary));
0662
0663 if (!$result)
0664 {
0665 return false;
0666 }
0667 }
0668 }
0669
0670 return true;
0671 }
0672
0673 /**
0674 * Function for validating values
0675 * @access private
0676 */
0677 function _sql_validate_value($var)
0678 {
0679 if (is_null($var))
0680 {
0681 return 'NULL';
0682 }
0683 else if (is_string($var))
0684 {
0685 return "'" . $this->sql_escape($var) . "'";
0686 }
0687 else
0688 {
0689 return (is_bool($var)) ? intval($var) : $var;
0690 }
0691 }
0692
0693 /**
0694 * {@inheritDoc}
0695 */
0696 function sql_build_query($query, $array)
0697 {
0698 $sql = '';
0699 switch ($query)
0700 {
0701 case 'SELECT':
0702 case 'SELECT_DISTINCT';
0703
0704 $sql = str_replace('_', ' ', $query) . ' ' . $array['SELECT'] . ' FROM ';
0705
0706 // Build table array. We also build an alias array for later checks.
0707 $table_array = $aliases = array();
0708 $used_multi_alias = false;
0709
0710 foreach ($array['FROM'] as $table_name => $alias)
0711 {
0712 if (is_array($alias))
0713 {
0714 $used_multi_alias = true;
0715
0716 foreach ($alias as $multi_alias)
0717 {
0718 $table_array[] = $table_name . ' ' . $multi_alias;
0719 $aliases[] = $multi_alias;
0720 }
0721 }
0722 else
0723 {
0724 $table_array[] = $table_name . ' ' . $alias;
0725 $aliases[] = $alias;
0726 }
0727 }
0728
0729 // We run the following code to determine if we need to re-order the table array. ;)
0730 // The reason for this is that for multi-aliased tables (two equal tables) in the FROM statement the last table need to match the first comparison.
0731 // DBMS who rely on this: Oracle, PostgreSQL and MSSQL. For all other DBMS it makes absolutely no difference in which order the table is.
0732 if (!empty($array['LEFT_JOIN']) && sizeof($array['FROM']) > 1 && $used_multi_alias !== false)
0733 {
0734 // Take first LEFT JOIN
0735 $join = current($array['LEFT_JOIN']);
0736
0737 // Determine the table used there (even if there are more than one used, we only want to have one
0738 preg_match('/(' . implode('|', $aliases) . ')\.[^\s]+/U', str_replace(array('(', ')', 'AND', 'OR', ' '), '', $join['ON']), $matches);
0739
0740 // If there is a first join match, we need to make sure the table order is correct
0741 if (!empty($matches[1]))
0742 {
0743 $first_join_match = trim($matches[1]);
0744 $table_array = $last = array();
0745
0746 foreach ($array['FROM'] as $table_name => $alias)
0747 {
0748 if (is_array($alias))
0749 {
0750 foreach ($alias as $multi_alias)
0751 {
0752 ($multi_alias === $first_join_match) ? $last[] = $table_name . ' ' . $multi_alias : $table_array[] = $table_name . ' ' . $multi_alias;
0753 }
0754 }
0755 else
0756 {
0757 ($alias === $first_join_match) ? $last[] = $table_name . ' ' . $alias : $table_array[] = $table_name . ' ' . $alias;
0758 }
0759 }
0760
0761 $table_array = array_merge($table_array, $last);
0762 }
0763 }
0764
0765 $sql .= $this->_sql_custom_build('FROM', implode(' CROSS JOIN ', $table_array));
0766
0767 if (!empty($array['LEFT_JOIN']))
0768 {
0769 foreach ($array['LEFT_JOIN'] as $join)
0770 {
0771 $sql .= ' LEFT JOIN ' . key($join['FROM']) . ' ' . current($join['FROM']) . ' ON (' . $join['ON'] . ')';
0772 }
0773 }
0774
0775 if (!empty($array['WHERE']))
0776 {
0777 $sql .= ' WHERE ' . $this->_sql_custom_build('WHERE', $array['WHERE']);
0778 }
0779
0780 if (!empty($array['GROUP_BY']))
0781 {
0782 $sql .= ' GROUP BY ' . $array['GROUP_BY'];
0783 }
0784
0785 if (!empty($array['ORDER_BY']))
0786 {
0787 $sql .= ' ORDER BY ' . $array['ORDER_BY'];
0788 }
0789
0790 break;
0791 }
0792
0793 return $sql;
0794 }
0795
0796 /**
0797 * {@inheritDoc}
0798 */
0799 function sql_error($sql = '')
0800 {
0801 global $auth, $user, $config;
0802
0803 // Set var to retrieve errored status
0804 $this->sql_error_triggered = true;
0805 $this->sql_error_sql = $sql;
0806
0807 $this->sql_error_returned = $this->_sql_error();
0808
0809 if (!$this->return_on_error)
0810 {
0811 $message = 'SQL ERROR [ ' . $this->sql_layer . ' ]<br /><br />' . $this->sql_error_returned['message'] . ' [' . $this->sql_error_returned['code'] . ']';
0812
0813 // Show complete SQL error and path to administrators only
0814 // Additionally show complete error on installation or if extended debug mode is enabled
0815 // The DEBUG constant is for development only!
0816 if ((isset($auth) && $auth->acl_get('a_')) || defined('IN_INSTALL') || defined('DEBUG'))
0817 {
0818 $message .= ($sql) ? '<br /><br />SQL<br /><br />' . htmlspecialchars($sql) : '';
0819 }
0820 else
0821 {
0822 // If error occurs in initiating the session we need to use a pre-defined language string
0823 // This could happen if the connection could not be established for example (then we are not able to grab the default language)
0824 if (!isset($user->lang['SQL_ERROR_OCCURRED']))
0825 {
0826 $message .= '<br /><br />An sql error occurred while fetching this page. Please contact an administrator if this problem persists.';
0827 }
0828 else
0829 {
0830 if (!empty($config['board_contact']))
0831 {
0832 $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
0833 }
0834 else
0835 {
0836 $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '', '');
0837 }
0838 }
0839 }
0840
0841 if ($this->transaction)
0842 {
0843 $this->sql_transaction('rollback');
0844 }
0845
0846 if (strlen($message) > 1024)
0847 {
0848 // We need to define $msg_long_text here to circumvent text stripping.
0849 global $msg_long_text;
0850 $msg_long_text = $message;
0851
0852 trigger_error(false, E_USER_ERROR);
0853 }
0854
0855 trigger_error($message, E_USER_ERROR);
0856 }
0857
0858 if ($this->transaction)
0859 {
0860 $this->sql_transaction('rollback');
0861 }
0862
0863 return $this->sql_error_returned;
0864 }
0865
0866 /**
0867 * {@inheritDoc}
0868 */
0869 function sql_report($mode, $query = '')
0870 {
0871 global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper, $user;
0872 global $request;
0873
0874 if (is_object($request) && !$request->variable('explain', false))
0875 {
0876 return false;
0877 }
0878
0879 if (!$query && $this->query_hold != '')
0880 {
0881 $query = $this->query_hold;
0882 }
0883
0884 switch ($mode)
0885 {
0886 case 'display':
0887 if (!empty($cache))
0888 {
0889 $cache->unload();
0890 }
0891 $this->sql_close();
0892
0893 $mtime = explode(' ', microtime());
0894 $totaltime = $mtime[0] + $mtime[1] - $starttime;
0895
0896 echo '<!DOCTYPE html>
0897 <html dir="ltr">
0898 <head>
0899 <meta charset="utf-8">
0900 <title>SQL Report</title>
0901 <link href="' . htmlspecialchars($phpbb_path_helper->update_web_root_path($phpbb_root_path) . $phpbb_path_helper->get_adm_relative_path()) . 'style/admin.css" rel="stylesheet" type="text/css" media="screen" />
0902 </head>
0903 <body id="errorpage">
0904 <div id="wrap">
0905 <div id="page-header">
0906 <a href="' . build_url('explain') . '">Return to previous page</a>
0907 </div>
0908 <div id="page-body">
0909 <div id="acp">
0910 <div class="panel">
0911 <span class="corners-top"><span></span></span>
0912 <div id="content">
0913 <h1>SQL Report</h1>
0914 <br />
0915 <p><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries['normal']} queries" . (($this->num_queries['cached']) ? " + {$this->num_queries['cached']} " . (($this->num_queries['cached'] == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></p>
0916
0917 <p>Time spent on ' . $this->sql_layer . ' queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></p>
0918
0919 <br /><br />
0920 ' . $this->sql_report . '
0921 </div>
0922 <span class="corners-bottom"><span></span></span>
0923 </div>
0924 </div>
0925 </div>
0926 <div id="page-footer">
0927 Powered by <a href="https://www.phpbb.com/">phpBB</a>® Forum Software © phpBB Limited
0928 </div>
0929 </div>
0930 </body>
0931 </html>';
0932
0933 exit_handler();
0934
0935 break;
0936
0937 case 'stop':
0938 $endtime = explode(' ', microtime());
0939 $endtime = $endtime[0] + $endtime[1];
0940
0941 $this->sql_report .= '
0942
0943 <table cellspacing="1">
0944 <thead>
0945 <tr>
0946 <th>Query #' . $this->num_queries['total'] . '</th>
0947 </tr>
0948 </thead>
0949 <tbody>
0950 <tr>
0951 <td class="row3"><textarea style="font-family:\'Courier New\',monospace;width:99%" rows="5" cols="10">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td>
0952 </tr>
0953 </tbody>
0954 </table>
0955
0956 ' . $this->html_hold . '
0957
0958 <p style="text-align: center;">
0959 ';
0960
0961 if ($this->query_result)
0962 {
0963 if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
0964 {
0965 $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows($this->query_result) . '</b> | ';
0966 }
0967 $this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $this->curtime) . 's</b>';
0968 }
0969 else
0970 {
0971 $error = $this->sql_error();
0972 $this->sql_report .= '<b style="color: red">FAILED</b> - ' . $this->sql_layer . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
0973 }
0974
0975 $this->sql_report .= '</p><br /><br />';
0976
0977 $this->sql_time += $endtime - $this->curtime;
0978 break;
0979
0980 case 'start':
0981 $this->query_hold = $query;
0982 $this->html_hold = '';
0983
0984 $this->_sql_report($mode, $query);
0985
0986 $this->curtime = explode(' ', microtime());
0987 $this->curtime = $this->curtime[0] + $this->curtime[1];
0988
0989 break;
0990
0991 case 'add_select_row':
0992
0993 $html_table = func_get_arg(2);
0994 $row = func_get_arg(3);
0995
0996 if (!$html_table && sizeof($row))
0997 {
0998 $html_table = true;
0999 $this->html_hold .= '<table cellspacing="1"><tr>';
1000
1001 foreach (array_keys($row) as $val)
1002 {
1003 $this->html_hold .= '<th>' . (($val) ? ucwords(str_replace('_', ' ', $val)) : ' ') . '</th>';
1004 }
1005 $this->html_hold .= '</tr>';
1006 }
1007 $this->html_hold .= '<tr>';
1008
1009 $class = 'row1';
1010 foreach (array_values($row) as $val)
1011 {
1012 $class = ($class == 'row1') ? 'row2' : 'row1';
1013 $this->html_hold .= '<td class="' . $class . '">' . (($val) ? $val : ' ') . '</td>';
1014 }
1015 $this->html_hold .= '</tr>';
1016
1017 return $html_table;
1018
1019 break;
1020
1021 case 'fromcache':
1022
1023 $this->_sql_report($mode, $query);
1024
1025 break;
1026
1027 case 'record_fromcache':
1028
1029 $endtime = func_get_arg(2);
1030 $splittime = func_get_arg(3);
1031
1032 $time_cache = $endtime - $this->curtime;
1033 $time_db = $splittime - $endtime;
1034 $color = ($time_db > $time_cache) ? 'green' : 'red';
1035
1036 $this->sql_report .= '<table cellspacing="1"><thead><tr><th>Query results obtained from the cache</th></tr></thead><tbody><tr>';
1037 $this->sql_report .= '<td class="row3"><textarea style="font-family:\'Courier New\',monospace;width:99%" rows="5" cols="10">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></tbody></table>';
1038 $this->sql_report .= '<p style="text-align: center;">';
1039 $this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p><br /><br />';
1040
1041 // Pad the start time to not interfere with page timing
1042 $starttime += $time_db;
1043
1044 break;
1045
1046 default:
1047
1048 $this->_sql_report($mode, $query);
1049
1050 break;
1051 }
1052
1053 return true;
1054 }
1055
1056 /**
1057 * {@inheritDoc}
1058 */
1059 function get_estimated_row_count($table_name)
1060 {
1061 return $this->get_row_count($table_name);
1062 }
1063
1064 /**
1065 * {@inheritDoc}
1066 */
1067 function get_row_count($table_name)
1068 {
1069 $sql = 'SELECT COUNT(*) AS rows_total
1070 FROM ' . $this->sql_escape($table_name);
1071 $result = $this->sql_query($sql);
1072 $rows_total = $this->sql_fetchfield('rows_total');
1073 $this->sql_freeresult($result);
1074
1075 return $rows_total;
1076 }
1077 }
1078