Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

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: 27.30 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      const LOGICAL_OP = 0;
0070      const STATEMENTS = 1;
0071      const LEFT_STMT = 0;
0072      const COMPARE_OP = 1;
0073      const RIGHT_STMT = 2;
0074      const SUBQUERY_OP = 3;
0075      const SUBQUERY_SELECT_TYPE = 4;
0076      const SUBQUERY_BUILD = 5;
0077   
0078      /**
0079      * Constructor
0080      */
0081      function __construct()
0082      {
0083          $this->num_queries = array(
0084              'cached'    => 0,
0085              'normal'    => 0,
0086              'total'        => 0,
0087          );
0088   
0089          // Fill default sql layer based on the class being called.
0090          // This can be changed by the specified layer itself later if needed.
0091          $this->sql_layer = substr(get_class($this), strlen('phpbb\db\driver\\'));
0092   
0093          // Do not change this please! This variable is used to easy the use of it - and is hardcoded.
0094          $this->any_char = chr(0) . '%';
0095          $this->one_char = chr(0) . '_';
0096      }
0097   
0098      /**
0099      * {@inheritdoc}
0100      */
0101      public function get_sql_layer()
0102      {
0103          return $this->sql_layer;
0104      }
0105   
0106      /**
0107      * {@inheritdoc}
0108      */
0109      public function get_db_name()
0110      {
0111          return $this->dbname;
0112      }
0113   
0114      /**
0115      * {@inheritdoc}
0116      */
0117      public function get_any_char()
0118      {
0119          return $this->any_char;
0120      }
0121   
0122      /**
0123      * {@inheritdoc}
0124      */
0125      public function get_one_char()
0126      {
0127          return $this->one_char;
0128      }
0129   
0130      /**
0131      * {@inheritdoc}
0132      */
0133      public function get_db_connect_id()
0134      {
0135          return $this->db_connect_id;
0136      }
0137   
0138      /**
0139      * {@inheritdoc}
0140      */
0141      public function get_sql_error_triggered()
0142      {
0143          return $this->sql_error_triggered;
0144      }
0145   
0146      /**
0147      * {@inheritdoc}
0148      */
0149      public function get_sql_error_sql()
0150      {
0151          return $this->sql_error_sql;
0152      }
0153   
0154      /**
0155      * {@inheritdoc}
0156      */
0157      public function get_transaction()
0158      {
0159          return $this->transaction;
0160      }
0161   
0162      /**
0163      * {@inheritdoc}
0164      */
0165      public function get_sql_time()
0166      {
0167          return $this->sql_time;
0168      }
0169   
0170      /**
0171      * {@inheritdoc}
0172      */
0173      public function get_sql_error_returned()
0174      {
0175          return $this->sql_error_returned;
0176      }
0177   
0178      /**
0179      * {@inheritdoc}
0180      */
0181      public function get_multi_insert()
0182      {
0183          return $this->multi_insert;
0184      }
0185   
0186      /**
0187      * {@inheritdoc}
0188      */
0189      public function set_multi_insert($multi_insert)
0190      {
0191          $this->multi_insert = $multi_insert;
0192      }
0193   
0194      /**
0195      * {@inheritDoc}
0196      */
0197      function sql_return_on_error($fail = false)
0198      {
0199          $this->sql_error_triggered = false;
0200          $this->sql_error_sql = '';
0201   
0202          $this->return_on_error = $fail;
0203      }
0204   
0205      /**
0206      * {@inheritDoc}
0207      */
0208      function sql_num_queries($cached = false)
0209      {
0210          return ($cached) ? $this->num_queries['cached'] : $this->num_queries['normal'];
0211      }
0212   
0213      /**
0214      * {@inheritDoc}
0215      */
0216      function sql_add_num_queries($cached = false)
0217      {
0218          $this->num_queries['cached'] += ($cached !== false) ? 1 : 0;
0219          $this->num_queries['normal'] += ($cached !== false) ? 0 : 1;
0220          $this->num_queries['total'] += 1;
0221      }
0222   
0223      /**
0224      * {@inheritDoc}
0225      */
0226      function sql_close()
0227      {
0228          if (!$this->db_connect_id)
0229          {
0230              return false;
0231          }
0232   
0233          if ($this->transaction)
0234          {
0235              do
0236              {
0237                  $this->sql_transaction('commit');
0238              }
0239              while ($this->transaction);
0240          }
0241   
0242          foreach ($this->open_queries as $query_id)
0243          {
0244              $this->sql_freeresult($query_id);
0245          }
0246   
0247          // Connection closed correctly. Set db_connect_id to false to prevent errors
0248          if ($result = $this->_sql_close())
0249          {
0250              $this->db_connect_id = false;
0251          }
0252   
0253          return $result;
0254      }
0255   
0256      /**
0257      * {@inheritDoc}
0258      */
0259      function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
0260      {
0261          if (empty($query))
0262          {
0263              return false;
0264          }
0265   
0266          // Never use a negative total or offset
0267          $total = ($total < 0) ? 0 : $total;
0268          $offset = ($offset < 0) ? 0 : $offset;
0269   
0270          return $this->_sql_query_limit($query, $total, $offset, $cache_ttl);
0271      }
0272   
0273      /**
0274      * {@inheritDoc}
0275      */
0276      function sql_fetchrowset($query_id = false)
0277      {
0278          if ($query_id === false)
0279          {
0280              $query_id = $this->query_result;
0281          }
0282   
0283          if ($query_id)
0284          {
0285              $result = array();
0286              while ($row = $this->sql_fetchrow($query_id))
0287              {
0288                  $result[] = $row;
0289              }
0290   
0291              return $result;
0292          }
0293   
0294          return false;
0295      }
0296   
0297      /**
0298      * {@inheritDoc}
0299      */
0300      function sql_rowseek($rownum, &$query_id)
0301      {
0302          global $cache;
0303   
0304          if ($query_id === false)
0305          {
0306              $query_id = $this->query_result;
0307          }
0308   
0309          if ($cache && $cache->sql_exists($query_id))
0310          {
0311              return $cache->sql_rowseek($rownum, $query_id);
0312          }
0313   
0314          if (!$query_id)
0315          {
0316              return false;
0317          }
0318   
0319          $this->sql_freeresult($query_id);
0320          $query_id = $this->sql_query($this->last_query_text);
0321   
0322          if (!$query_id)
0323          {
0324              return false;
0325          }
0326   
0327          // We do not fetch the row for rownum == 0 because then the next resultset would be the second row
0328          for ($i = 0; $i < $rownum; $i++)
0329          {
0330              if (!$this->sql_fetchrow($query_id))
0331              {
0332                  return false;
0333              }
0334          }
0335   
0336          return true;
0337      }
0338   
0339      /**
0340      * {@inheritDoc}
0341      */
0342      function sql_fetchfield($field, $rownum = false, $query_id = false)
0343      {
0344          global $cache;
0345   
0346          if ($query_id === false)
0347          {
0348              $query_id = $this->query_result;
0349          }
0350   
0351          if ($query_id)
0352          {
0353              if ($rownum !== false)
0354              {
0355                  $this->sql_rowseek($rownum, $query_id);
0356              }
0357   
0358              if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
0359              {
0360                  return $cache->sql_fetchfield($query_id, $field);
0361              }
0362   
0363              $row = $this->sql_fetchrow($query_id);
0364              return (isset($row[$field])) ? $row[$field] : false;
0365          }
0366   
0367          return false;
0368      }
0369   
0370      /**
0371      * {@inheritDoc}
0372      */
0373      function sql_like_expression($expression)
0374      {
0375          $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
0376          $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
0377   
0378          return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\'');
0379      }
0380   
0381      /**
0382      * {@inheritDoc}
0383      */
0384      function sql_not_like_expression($expression)
0385      {
0386          $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
0387          $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
0388   
0389          return $this->_sql_not_like_expression('NOT LIKE \'' . $this->sql_escape($expression) . '\'');
0390      }
0391   
0392      /**
0393      * {@inheritDoc}
0394      */
0395      public function sql_case($condition, $action_true, $action_false = false)
0396      {
0397          $sql_case = 'CASE WHEN ' . $condition;
0398          $sql_case .= ' THEN ' . $action_true;
0399          $sql_case .= ($action_false !== false) ? ' ELSE ' . $action_false : '';
0400          $sql_case .= ' END';
0401          return $sql_case;
0402      }
0403   
0404      /**
0405      * {@inheritDoc}
0406      */
0407      public function sql_concatenate($expr1, $expr2)
0408      {
0409          return $expr1 . ' || ' . $expr2;
0410      }
0411   
0412      /**
0413      * {@inheritDoc}
0414      */
0415      function sql_buffer_nested_transactions()
0416      {
0417          return false;
0418      }
0419   
0420      /**
0421      * {@inheritDoc}
0422      */
0423      function sql_transaction($status = 'begin')
0424      {
0425          switch ($status)
0426          {
0427              case 'begin':
0428                  // If we are within a transaction we will not open another one, but enclose the current one to not loose data (preventing auto commit)
0429                  if ($this->transaction)
0430                  {
0431                      $this->transactions++;
0432                      return true;
0433                  }
0434   
0435                  $result = $this->_sql_transaction('begin');
0436   
0437                  if (!$result)
0438                  {
0439                      $this->sql_error();
0440                  }
0441   
0442                  $this->transaction = true;
0443              break;
0444   
0445              case 'commit':
0446                  // If there was a previously opened transaction we do not commit yet...
0447                  // but count back the number of inner transactions
0448                  if ($this->transaction && $this->transactions)
0449                  {
0450                      $this->transactions--;
0451                      return true;
0452                  }
0453   
0454                  // Check if there is a transaction (no transaction can happen if
0455                  // there was an error, with a combined rollback and error returning enabled)
0456                  // This implies we have transaction always set for autocommit db's
0457                  if (!$this->transaction)
0458                  {
0459                      return false;
0460                  }
0461   
0462                  $result = $this->_sql_transaction('commit');
0463   
0464                  if (!$result)
0465                  {
0466                      $this->sql_error();
0467                  }
0468   
0469                  $this->transaction = false;
0470                  $this->transactions = 0;
0471              break;
0472   
0473              case 'rollback':
0474                  $result = $this->_sql_transaction('rollback');
0475                  $this->transaction = false;
0476                  $this->transactions = 0;
0477              break;
0478   
0479              default:
0480                  $result = $this->_sql_transaction($status);
0481              break;
0482          }
0483   
0484          return $result;
0485      }
0486   
0487      /**
0488      * {@inheritDoc}
0489      */
0490      function sql_build_array($query, $assoc_ary = false)
0491      {
0492          if (!is_array($assoc_ary))
0493          {
0494              return false;
0495          }
0496   
0497          $fields = $values = array();
0498   
0499          if ($query == 'INSERT' || $query == 'INSERT_SELECT')
0500          {
0501              foreach ($assoc_ary as $key => $var)
0502              {
0503                  $fields[] = $key;
0504   
0505                  if (is_array($var) && is_string($var[0]))
0506                  {
0507                      // This is used for INSERT_SELECT(s)
0508                      $values[] = $var[0];
0509                  }
0510                  else
0511                  {
0512                      $values[] = $this->_sql_validate_value($var);
0513                  }
0514              }
0515   
0516              $query = ($query == 'INSERT') ? ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')' : ' (' . implode(', ', $fields) . ') SELECT ' . implode(', ', $values) . ' ';
0517          }
0518          else if ($query == 'MULTI_INSERT')
0519          {
0520              trigger_error('The MULTI_INSERT query value is no longer supported. Please use sql_multi_insert() instead.', E_USER_ERROR);
0521          }
0522          else if ($query == 'UPDATE' || $query == 'SELECT' || $query == 'DELETE')
0523          {
0524              $values = array();
0525              foreach ($assoc_ary as $key => $var)
0526              {
0527                  $values[] = "$key = " . $this->_sql_validate_value($var);
0528              }
0529              $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
0530          }
0531   
0532          return $query;
0533      }
0534   
0535      /**
0536      * {@inheritDoc}
0537      */
0538      function sql_in_set($field, $array, $negate = false, $allow_empty_set = false)
0539      {
0540          if (!sizeof($array))
0541          {
0542              if (!$allow_empty_set)
0543              {
0544                  // Print the backtrace to help identifying the location of the problematic code
0545                  $this->sql_error('No values specified for SQL IN comparison');
0546              }
0547              else
0548              {
0549                  // NOT IN () actually means everything so use a tautology
0550                  if ($negate)
0551                  {
0552                      return '1=1';
0553                  }
0554                  // IN () actually means nothing so use a contradiction
0555                  else
0556                  {
0557                      return '1=0';
0558                  }
0559              }
0560          }
0561   
0562          if (!is_array($array))
0563          {
0564              $array = array($array);
0565          }
0566   
0567          if (sizeof($array) == 1)
0568          {
0569              @reset($array);
0570              $var = current($array);
0571   
0572              return $field . ($negate ? ' <> ' : ' = ') . $this->_sql_validate_value($var);
0573          }
0574          else
0575          {
0576              return $field . ($negate ? ' NOT IN ' : ' IN ') . '(' . implode(', ', array_map(array($this, '_sql_validate_value'), $array)) . ')';
0577          }
0578      }
0579   
0580      /**
0581      * {@inheritDoc}
0582      */
0583      function sql_bit_and($column_name, $bit, $compare = '')
0584      {
0585          if (method_exists($this, '_sql_bit_and'))
0586          {
0587              return $this->_sql_bit_and($column_name, $bit, $compare);
0588          }
0589   
0590          return $column_name . ' & ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
0591      }
0592   
0593      /**
0594      * {@inheritDoc}
0595      */
0596      function sql_bit_or($column_name, $bit, $compare = '')
0597      {
0598          if (method_exists($this, '_sql_bit_or'))
0599          {
0600              return $this->_sql_bit_or($column_name, $bit, $compare);
0601          }
0602   
0603          return $column_name . ' | ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
0604      }
0605   
0606      /**
0607      * {@inheritDoc}
0608      */
0609      function cast_expr_to_bigint($expression)
0610      {
0611          return $expression;
0612      }
0613   
0614      /**
0615      * {@inheritDoc}
0616      */
0617      function cast_expr_to_string($expression)
0618      {
0619          return $expression;
0620      }
0621   
0622      /**
0623      * {@inheritDoc}
0624      */
0625      function sql_lower_text($column_name)
0626      {
0627          return "LOWER($column_name)";
0628      }
0629   
0630      /**
0631      * {@inheritDoc}
0632      */
0633      function sql_multi_insert($table, $sql_ary)
0634      {
0635          if (!sizeof($sql_ary))
0636          {
0637              return false;
0638          }
0639   
0640          if ($this->multi_insert)
0641          {
0642              $ary = array();
0643              foreach ($sql_ary as $id => $_sql_ary)
0644              {
0645                  // If by accident the sql array is only one-dimensional we build a normal insert statement
0646                  if (!is_array($_sql_ary))
0647                  {
0648                      return $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $sql_ary));
0649                  }
0650   
0651                  $values = array();
0652                  foreach ($_sql_ary as $key => $var)
0653                  {
0654                      $values[] = $this->_sql_validate_value($var);
0655                  }
0656                  $ary[] = '(' . implode(', ', $values) . ')';
0657              }
0658   
0659              return $this->sql_query('INSERT INTO ' . $table . ' ' . ' (' . implode(', ', array_keys($sql_ary[0])) . ') VALUES ' . implode(', ', $ary));
0660          }
0661          else
0662          {
0663              foreach ($sql_ary as $ary)
0664              {
0665                  if (!is_array($ary))
0666                  {
0667                      return false;
0668                  }
0669   
0670                  $result = $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $ary));
0671   
0672                  if (!$result)
0673                  {
0674                      return false;
0675                  }
0676              }
0677          }
0678   
0679          return true;
0680      }
0681   
0682      /**
0683      * Function for validating values
0684      * @access private
0685      */
0686      function _sql_validate_value($var)
0687      {
0688          if (is_null($var))
0689          {
0690              return 'NULL';
0691          }
0692          else if (is_string($var))
0693          {
0694              return "'" . $this->sql_escape($var) . "'";
0695          }
0696          else
0697          {
0698              return (is_bool($var)) ? intval($var) : $var;
0699          }
0700      }
0701   
0702      /**
0703      * {@inheritDoc}
0704      */
0705      function sql_build_query($query, $array)
0706      {
0707          $sql = '';
0708          switch ($query)
0709          {
0710              case 'SELECT':
0711              case 'SELECT_DISTINCT';
0712   
0713                  $sql = str_replace('_', ' ', $query) . ' ' . $array['SELECT'] . ' FROM ';
0714   
0715                  // Build table array. We also build an alias array for later checks.
0716                  $table_array = $aliases = array();
0717                  $used_multi_alias = false;
0718   
0719                  foreach ($array['FROM'] as $table_name => $alias)
0720                  {
0721                      if (is_array($alias))
0722                      {
0723                          $used_multi_alias = true;
0724   
0725                          foreach ($alias as $multi_alias)
0726                          {
0727                              $table_array[] = $table_name . ' ' . $multi_alias;
0728                              $aliases[] = $multi_alias;
0729                          }
0730                      }
0731                      else
0732                      {
0733                          $table_array[] = $table_name . ' ' . $alias;
0734                          $aliases[] = $alias;
0735                      }
0736                  }
0737   
0738                  // We run the following code to determine if we need to re-order the table array. ;)
0739                  // 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.
0740                  // DBMS who rely on this: Oracle, PostgreSQL and MSSQL. For all other DBMS it makes absolutely no difference in which order the table is.
0741                  if (!empty($array['LEFT_JOIN']) && sizeof($array['FROM']) > 1 && $used_multi_alias !== false)
0742                  {
0743                      // Take first LEFT JOIN
0744                      $join = current($array['LEFT_JOIN']);
0745   
0746                      // Determine the table used there (even if there are more than one used, we only want to have one
0747                      preg_match('/(' . implode('|', $aliases) . ')\.[^\s]+/U', str_replace(array('(', ')', 'AND', 'OR', ' '), '', $join['ON']), $matches);
0748   
0749                      // If there is a first join match, we need to make sure the table order is correct
0750                      if (!empty($matches[1]))
0751                      {
0752                          $first_join_match = trim($matches[1]);
0753                          $table_array = $last = array();
0754   
0755                          foreach ($array['FROM'] as $table_name => $alias)
0756                          {
0757                              if (is_array($alias))
0758                              {
0759                                  foreach ($alias as $multi_alias)
0760                                  {
0761                                      ($multi_alias === $first_join_match) ? $last[] = $table_name . ' ' . $multi_alias : $table_array[] = $table_name . ' ' . $multi_alias;
0762                                  }
0763                              }
0764                              else
0765                              {
0766                                  ($alias === $first_join_match) ? $last[] = $table_name . ' ' . $alias : $table_array[] = $table_name . ' ' . $alias;
0767                              }
0768                          }
0769   
0770                          $table_array = array_merge($table_array, $last);
0771                      }
0772                  }
0773   
0774                  $sql .= $this->_sql_custom_build('FROM', implode(' CROSS JOIN ', $table_array));
0775   
0776                  if (!empty($array['LEFT_JOIN']))
0777                  {
0778                      foreach ($array['LEFT_JOIN'] as $join)
0779                      {
0780                          $sql .= ' LEFT JOIN ' . key($join['FROM']) . ' ' . current($join['FROM']) . ' ON (' . $join['ON'] . ')';
0781                      }
0782                  }
0783   
0784                  if (!empty($array['WHERE']))
0785                  {
0786                      $sql .= ' WHERE ';
0787   
0788                      if (is_array($array['WHERE']))
0789                      {
0790                          $sql_where = $this->_process_boolean_tree_first($array['WHERE']);
0791                      }
0792                      else
0793                      {
0794                          $sql_where = $array['WHERE'];
0795                      }
0796   
0797                      $sql .= $this->_sql_custom_build('WHERE', $sql_where);
0798                  }
0799   
0800                  if (!empty($array['GROUP_BY']))
0801                  {
0802                      $sql .= ' GROUP BY ' . $array['GROUP_BY'];
0803                  }
0804   
0805                  if (!empty($array['ORDER_BY']))
0806                  {
0807                      $sql .= ' ORDER BY ' . $array['ORDER_BY'];
0808                  }
0809   
0810              break;
0811          }
0812   
0813          return $sql;
0814      }
0815   
0816   
0817      protected function _process_boolean_tree_first($operations_ary)
0818      {
0819          // In cases where an array exists but there is no head condition,
0820          // it should be because there's only 1 WHERE clause. This seems the best way to deal with it.
0821          if ($operations_ary[self::LOGICAL_OP] !== 'AND' &&
0822              $operations_ary[self::LOGICAL_OP] !== 'OR')
0823          {
0824              $operations_ary = array('AND', array($operations_ary));
0825          }
0826          return $this->_process_boolean_tree($operations_ary) . "\n";
0827      }
0828   
0829      protected function _process_boolean_tree($operations_ary)
0830      {
0831          $operation = $operations_ary[self::LOGICAL_OP];
0832   
0833          foreach ($operations_ary[self::STATEMENTS] as &$condition)
0834          {
0835              switch ($condition[self::LOGICAL_OP])
0836              {
0837                  case 'AND':
0838                  case 'OR':
0839   
0840                      $condition = ' ( ' . $this->_process_boolean_tree($condition) . ') ';
0841   
0842                  break;
0843                  case 'NOT':
0844   
0845                      $condition = ' NOT (' . $this->_process_boolean_tree($condition) . ') ';
0846   
0847                  break;
0848   
0849                  default:
0850   
0851                      switch (sizeof($condition))
0852                      {
0853                          case 3:
0854   
0855                              // Typical 3 element clause with {left hand} {operator} {right hand}
0856                              switch ($condition[self::COMPARE_OP])
0857                              {
0858                                  case 'IN':
0859                                  case 'NOT_IN':
0860   
0861                                      // As this is used with an IN, assume it is a set of elements for sql_in_set()
0862                                      $condition = $this->sql_in_set($condition[self::LEFT_STMT], $condition[self::RIGHT_STMT], $condition[self::COMPARE_OP] === 'NOT_IN', true);
0863   
0864                                  break;
0865   
0866                                  case 'LIKE':
0867   
0868                                      $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_like_expression($condition[self::RIGHT_STMT]) . ' ';
0869   
0870                                  break;
0871   
0872                                  case 'NOT_LIKE':
0873   
0874                                      $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_not_like_expression($condition[self::RIGHT_STMT]) . ' ';
0875   
0876                                  break;
0877   
0878                                  case 'IS_NOT':
0879   
0880                                      $condition[self::COMPARE_OP] = 'IS NOT';
0881   
0882                                  // no break
0883                                  case 'IS':
0884   
0885                                      // If the value is NULL, the string of it is the empty string ('') which is not the intended result.
0886                                      // this should solve that
0887                                      if ($condition[self::RIGHT_STMT] === null)
0888                                      {
0889                                          $condition[self::RIGHT_STMT] = 'NULL';
0890                                      }
0891   
0892                                      $condition = implode(' ', $condition);
0893   
0894                                  break;
0895   
0896                                  default:
0897   
0898                                      $condition = implode(' ', $condition);
0899   
0900                                  break;
0901                              }
0902   
0903                          break;
0904   
0905                          case 5:
0906   
0907                              // Subquery with {left hand} {operator} {compare kind} {SELECT Kind } {Sub Query}
0908   
0909                              $condition = $condition[self::LEFT_STMT] . ' ' . $condition[self::COMPARE_OP] . ' ' . $condition[self::SUBQUERY_OP] . ' ( ';
0910                              $condition .= $this->sql_build_query($condition[self::SUBQUERY_SELECT_TYPE], $condition[self::SUBQUERY_BUILD]);
0911                              $condition .= ' )';
0912   
0913                          break;
0914   
0915                          default:
0916                              // This is an unpredicted clause setup. Just join all elements.
0917                              $condition = implode(' ', $condition);
0918   
0919                          break;
0920                      }
0921   
0922                  break;
0923              }
0924   
0925          }
0926   
0927          if ($operation === 'NOT')
0928          {
0929              $operations_ary =  implode("", $operations_ary[self::STATEMENTS]);
0930          }
0931          else
0932          {
0933              $operations_ary = implode(" \n    $operation ", $operations_ary[self::STATEMENTS]);
0934          }
0935   
0936          return $operations_ary;
0937      }
0938   
0939   
0940      /**
0941      * {@inheritDoc}
0942      */
0943      function sql_error($sql = '')
0944      {
0945          global $auth, $user, $config;
0946   
0947          // Set var to retrieve errored status
0948          $this->sql_error_triggered = true;
0949          $this->sql_error_sql = $sql;
0950   
0951          $this->sql_error_returned = $this->_sql_error();
0952   
0953          if (!$this->return_on_error)
0954          {
0955              $message = 'SQL ERROR [ ' . $this->sql_layer . ' ]<br /><br />' . $this->sql_error_returned['message'] . ' [' . $this->sql_error_returned['code'] . ']';
0956   
0957              // Show complete SQL error and path to administrators only
0958              // Additionally show complete error on installation or if extended debug mode is enabled
0959              // The DEBUG constant is for development only!
0960              if ((isset($auth) && $auth->acl_get('a_')) || defined('IN_INSTALL') || defined('DEBUG'))
0961              {
0962                  $message .= ($sql) ? '<br /><br />SQL<br /><br />' . htmlspecialchars($sql) : '';
0963              }
0964              else
0965              {
0966                  // If error occurs in initiating the session we need to use a pre-defined language string
0967                  // This could happen if the connection could not be established for example (then we are not able to grab the default language)
0968                  if (!isset($user->lang['SQL_ERROR_OCCURRED']))
0969                  {
0970                      $message .= '<br /><br />An sql error occurred while fetching this page. Please contact an administrator if this problem persists.';
0971                  }
0972                  else
0973                  {
0974                      if (!empty($config['board_contact']))
0975                      {
0976                          $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
0977                      }
0978                      else
0979                      {
0980                          $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '', '');
0981                      }
0982                  }
0983              }
0984   
0985              if ($this->transaction)
0986              {
0987                  $this->sql_transaction('rollback');
0988              }
0989   
0990              if (strlen($message) > 1024)
0991              {
0992                  // We need to define $msg_long_text here to circumvent text stripping.
0993                  global $msg_long_text;
0994                  $msg_long_text = $message;
0995   
0996                  trigger_error(false, E_USER_ERROR);
0997              }
0998   
0999              trigger_error($message, E_USER_ERROR);
1000          }
1001   
1002          if ($this->transaction)
1003          {
1004              $this->sql_transaction('rollback');
1005          }
1006   
1007          return $this->sql_error_returned;
1008      }
1009   
1010      /**
1011      * {@inheritDoc}
1012      */
1013      function sql_report($mode, $query = '')
1014      {
1015          global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper;
1016          global $request;
1017   
1018          if (is_object($request) && !$request->variable('explain', false))
1019          {
1020              return false;
1021          }
1022   
1023          if (!$query && $this->query_hold != '')
1024          {
1025              $query = $this->query_hold;
1026          }
1027   
1028          switch ($mode)
1029          {
1030              case 'display':
1031                  if (!empty($cache))
1032                  {
1033                      $cache->unload();
1034                  }
1035                  $this->sql_close();
1036   
1037                  $mtime = explode(' ', microtime());
1038                  $totaltime = $mtime[0] + $mtime[1] - $starttime;
1039   
1040                  echo '<!DOCTYPE html>
1041                      <html dir="ltr">
1042                      <head>
1043                          <meta charset="utf-8">
1044                          <meta http-equiv="X-UA-Compatible" content="IE=edge">
1045                          <title>SQL Report</title>
1046                          <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" />
1047                      </head>
1048                      <body id="errorpage">
1049                      <div id="wrap">
1050                          <div id="page-header">
1051                              <a href="' . build_url('explain') . '">Return to previous page</a>
1052                          </div>
1053                          <div id="page-body">
1054                              <div id="acp">
1055                              <div class="panel">
1056                                  <span class="corners-top"><span></span></span>
1057                                  <div id="content">
1058                                      <h1>SQL Report</h1>
1059                                      <br />
1060                                      <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>
1061   
1062                                      <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>
1063   
1064                                      <br /><br />
1065                                      ' . $this->sql_report . '
1066                                  </div>
1067                                  <span class="corners-bottom"><span></span></span>
1068                              </div>
1069                              </div>
1070                          </div>
1071                          <div id="page-footer">
1072                              Powered by <a href="https://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Limited
1073                          </div>
1074                      </div>
1075                      </body>
1076                      </html>';
1077   
1078                  exit_handler();
1079   
1080              break;
1081   
1082              case 'stop':
1083                  $endtime = explode(' ', microtime());
1084                  $endtime = $endtime[0] + $endtime[1];
1085   
1086                  $this->sql_report .= '
1087   
1088                      <table cellspacing="1">
1089                      <thead>
1090                      <tr>
1091                          <th>Query #' . $this->num_queries['total'] . '</th>
1092                      </tr>
1093                      </thead>
1094                      <tbody>
1095                      <tr>
1096                          <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>
1097                      </tr>
1098                      </tbody>
1099                      </table>
1100   
1101                      ' . $this->html_hold . '
1102   
1103                      <p style="text-align: center;">
1104                  ';
1105   
1106                  if ($this->query_result)
1107                  {
1108                      if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
1109                      {
1110                          $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows() . '</b> | ';
1111                      }
1112                      $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>';
1113                  }
1114                  else
1115                  {
1116                      $error = $this->sql_error();
1117                      $this->sql_report .= '<b style="color: red">FAILED</b> - ' . $this->sql_layer . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
1118                  }
1119   
1120                  $this->sql_report .= '</p><br /><br />';
1121   
1122                  $this->sql_time += $endtime - $this->curtime;
1123              break;
1124   
1125              case 'start':
1126                  $this->query_hold = $query;
1127                  $this->html_hold = '';
1128   
1129                  $this->_sql_report($mode, $query);
1130   
1131                  $this->curtime = explode(' ', microtime());
1132                  $this->curtime = $this->curtime[0] + $this->curtime[1];
1133   
1134              break;
1135   
1136              case 'add_select_row':
1137   
1138                  $html_table = func_get_arg(2);
1139                  $row = func_get_arg(3);
1140   
1141                  if (!$html_table && sizeof($row))
1142                  {
1143                      $html_table = true;
1144                      $this->html_hold .= '<table cellspacing="1"><tr>';
1145   
1146                      foreach (array_keys($row) as $val)
1147                      {
1148                          $this->html_hold .= '<th>' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
1149                      }
1150                      $this->html_hold .= '</tr>';
1151                  }
1152                  $this->html_hold .= '<tr>';
1153   
1154                  $class = 'row1';
1155                  foreach (array_values($row) as $val)
1156                  {
1157                      $class = ($class == 'row1') ? 'row2' : 'row1';
1158                      $this->html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
1159                  }
1160                  $this->html_hold .= '</tr>';
1161   
1162                  return $html_table;
1163   
1164              break;
1165   
1166              case 'fromcache':
1167   
1168                  $this->_sql_report($mode, $query);
1169   
1170              break;
1171   
1172              case 'record_fromcache':
1173   
1174                  $endtime = func_get_arg(2);
1175                  $splittime = func_get_arg(3);
1176   
1177                  $time_cache = $endtime - $this->curtime;
1178                  $time_db = $splittime - $endtime;
1179                  $color = ($time_db > $time_cache) ? 'green' : 'red';
1180   
1181                  $this->sql_report .= '<table cellspacing="1"><thead><tr><th>Query results obtained from the cache</th></tr></thead><tbody><tr>';
1182                  $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>';
1183                  $this->sql_report .= '<p style="text-align: center;">';
1184                  $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 />';
1185   
1186                  // Pad the start time to not interfere with page timing
1187                  $starttime += $time_db;
1188   
1189              break;
1190   
1191              default:
1192   
1193                  $this->_sql_report($mode, $query);
1194   
1195              break;
1196          }
1197   
1198          return true;
1199      }
1200   
1201      /**
1202      * {@inheritDoc}
1203      */
1204      function get_estimated_row_count($table_name)
1205      {
1206          return $this->get_row_count($table_name);
1207      }
1208   
1209      /**
1210      * {@inheritDoc}
1211      */
1212      function get_row_count($table_name)
1213      {
1214          $sql = 'SELECT COUNT(*) AS rows_total
1215              FROM ' . $this->sql_escape($table_name);
1216          $result = $this->sql_query($sql);
1217          $rows_total = $this->sql_fetchfield('rows_total');
1218          $this->sql_freeresult($result);
1219   
1220          return $rows_total;
1221      }
1222  }
1223