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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

driver.php

Zuletzt modifiziert: 09.10.2024, 12:54 - Dateigröße: 23.91 KiB


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>&reg; Forum Software &copy; 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)) : '&nbsp;') . '</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 : '&nbsp;') . '</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