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