[ Index ]

PHP Cross Reference of phpBB-3.3.12-deutsch

title

Body

[close]

/install/convert/ -> convertor.php (source)

   1  <?php
   2  /**
   3   *
   4   * This file is part of the phpBB Forum Software package.
   5   *
   6   * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7   * @license GNU General Public License, version 2 (GPL-2.0)
   8   *
   9   * For full copyright and license information, please see
  10   * the docs/CREDITS.txt file.
  11   *
  12   */
  13  
  14  namespace phpbb\convert;
  15  
  16  use phpbb\install\controller\helper;
  17  use phpbb\template\template;
  18  
  19  /**
  20   * Convertor backend class
  21   *
  22   * WARNING: This file did not meant to be present in a production environment, so moving this file to a location which
  23   *             is accessible after board installation might lead to security issues.
  24   */
  25  class convertor
  26  {
  27      /**
  28       * @var helper
  29       */
  30      protected $controller_helper;
  31  
  32      /**
  33       * @var \phpbb\filesystem\filesystem
  34       */
  35      protected $filesystem;
  36  
  37      /**
  38       * @var \phpbb\template\template
  39       */
  40      protected $template;
  41  
  42      /**
  43       * Constructor
  44       *
  45       * @param template    $template
  46       * @param helper    $controller_helper
  47       */
  48  	public function __construct(template $template, helper $controller_helper)
  49      {
  50          global $convert, $phpbb_filesystem;
  51  
  52          $this->template = $template;
  53          $this->filesystem = $phpbb_filesystem;
  54          $this->controller_helper = $controller_helper;
  55  
  56          $convert = new convert($this);
  57      }
  58  
  59      /**
  60       * The function which does the actual work (or dispatches it to the relevant places)
  61       */
  62  	function convert_data($converter)
  63      {
  64          global $user, $phpbb_root_path, $phpEx, $db, $lang, $config, $cache, $auth;
  65          global $convert, $convert_row, $message_parser, $skip_rows, $language;
  66          global $request, $phpbb_dispatcher, $phpbb_container;
  67  
  68          $phpbb_config_php_file = new \phpbb\config_php_file($phpbb_root_path, $phpEx);
  69          extract($phpbb_config_php_file->get_all());
  70  
  71          require_once($phpbb_root_path . 'includes/constants.' . $phpEx);
  72          require_once($phpbb_root_path . 'includes/functions_convert.' . $phpEx);
  73  
  74          $dbms = $phpbb_config_php_file->convert_30_dbms_to_31($dbms);
  75  
  76          /** @var \phpbb\db\driver\driver_interface $db */
  77          $db = new $dbms();
  78          $db->sql_connect($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, false, true);
  79          unset($dbpasswd);
  80  
  81          // We need to fill the config to let internal functions correctly work
  82          $config = new \phpbb\config\db($db, new \phpbb\cache\driver\dummy, CONFIG_TABLE);
  83  
  84          // Override a couple of config variables for the duration
  85          $config['max_quote_depth'] = 0;
  86  
  87          // @todo Need to confirm that max post length in source is <= max post length in destination or there may be interesting formatting issues
  88          $config['max_post_chars'] = $config['min_post_chars'] = 0;
  89  
  90          // Set up a user as well. We _should_ have enough of a database here at this point to do this
  91          // and it helps for any core code we call
  92          $user->session_begin();
  93          $user->page = $user->extract_current_page($phpbb_root_path);
  94  
  95          $convert->options = array();
  96          if (isset($config['convert_progress']))
  97          {
  98              $convert->options = unserialize($config['convert_progress']);
  99              $convert->options = array_merge($convert->options, unserialize($config['convert_db_server']), unserialize($config['convert_db_user']), unserialize($config['convert_options']));
 100          }
 101  
 102          // This information should have already been checked once, but do it again for safety
 103          if (empty($convert->options) || empty($convert->options['tag']) ||
 104              !isset($convert->options['dbms']) ||
 105              !isset($convert->options['dbhost']) ||
 106              !isset($convert->options['dbport']) ||
 107              !isset($convert->options['dbuser']) ||
 108              !isset($convert->options['dbpasswd']) ||
 109              !isset($convert->options['dbname']) ||
 110              !isset($convert->options['table_prefix']))
 111          {
 112              $this->error($user->lang['NO_CONVERT_SPECIFIED'], __LINE__, __FILE__);
 113          }
 114  
 115          $this->template->assign_var('S_CONV_IN_PROGRESS', true);
 116  
 117          // Make some short variables accessible, for easier referencing
 118          $convert->convertor_tag = basename($convert->options['tag']);
 119          $convert->src_dbms = $convert->options['dbms'];
 120          $convert->src_dbhost = $convert->options['dbhost'];
 121          $convert->src_dbport = $convert->options['dbport'];
 122          $convert->src_dbuser = $convert->options['dbuser'];
 123          $convert->src_dbpasswd = $convert->options['dbpasswd'];
 124          $convert->src_dbname = $convert->options['dbname'];
 125          $convert->src_table_prefix = $convert->options['table_prefix'];
 126  
 127          // initiate database connection to old db if old and new db differ
 128          global $src_db, $same_db;
 129          $src_db = $same_db = null;
 130          if ($convert->src_dbms != $dbms || $convert->src_dbhost != $dbhost || $convert->src_dbport != $dbport || $convert->src_dbname != $dbname || $convert->src_dbuser != $dbuser)
 131          {
 132              $dbms = $convert->src_dbms;
 133              /** @var \phpbb\db\driver\driver $src_db */
 134              $src_db = new $dbms();
 135              $src_db->sql_connect($convert->src_dbhost, $convert->src_dbuser, htmlspecialchars_decode($convert->src_dbpasswd, ENT_COMPAT), $convert->src_dbname, $convert->src_dbport, false, true);
 136              $same_db = false;
 137          }
 138          else
 139          {
 140              $src_db = $db;
 141              $same_db = true;
 142          }
 143  
 144          $convert->mysql_convert = false;
 145          switch ($src_db->sql_layer)
 146          {
 147              case 'sqlite3':
 148                  $convert->src_truncate_statement = 'DELETE FROM ';
 149                  break;
 150  
 151              case 'mysqli':
 152                  $convert->mysql_convert = true;
 153                  $convert->src_truncate_statement = 'TRUNCATE TABLE ';
 154                  break;
 155  
 156              default:
 157                  $convert->src_truncate_statement = 'TRUNCATE TABLE ';
 158                  break;
 159          }
 160  
 161          if ($convert->mysql_convert && !$same_db)
 162          {
 163              $src_db->sql_query("SET NAMES 'binary'");
 164          }
 165  
 166          switch ($db->get_sql_layer())
 167          {
 168              case 'sqlite3':
 169                  $convert->truncate_statement = 'DELETE FROM ';
 170                  break;
 171  
 172              default:
 173                  $convert->truncate_statement = 'TRUNCATE TABLE ';
 174                  break;
 175          }
 176  
 177          $get_info = false;
 178  
 179          // check security implications of direct inclusion
 180          if (!file_exists('./convertors/convert_' . $convert->convertor_tag . '.' . $phpEx))
 181          {
 182              $this->error($user->lang['CONVERT_NOT_EXIST'], __LINE__, __FILE__);
 183          }
 184  
 185          if (file_exists('./convertors/functions_' . $convert->convertor_tag . '.' . $phpEx))
 186          {
 187              include_once('./convertors/functions_' . $convert->convertor_tag . '.' . $phpEx);
 188          }
 189  
 190          $get_info = true;
 191          include('./convertors/convert_' . $convert->convertor_tag . '.' . $phpEx);
 192  
 193          // Map some variables...
 194          $convert->convertor_data = $convertor_data;
 195          $convert->tables = $tables;
 196          $convert->config_schema = $config_schema;
 197  
 198          // Now include the real data
 199          $get_info = false;
 200          include('./convertors/convert_' . $convert->convertor_tag . '.' . $phpEx);
 201  
 202          $convert->convertor_data = $convertor_data;
 203          $convert->tables = $tables;
 204          $convert->config_schema = $config_schema;
 205          $convert->convertor = $convertor;
 206  
 207          // The test_file is a file that should be present in the location of the old board.
 208          if (!file_exists($convert->options['forum_path'] . '/' . $test_file))
 209          {
 210              $this->error(sprintf($user->lang['COULD_NOT_FIND_PATH'], $convert->options['forum_path']), __LINE__, __FILE__);
 211          }
 212  
 213          $search_type = $config['search_type'];
 214  
 215          // For conversions we are a bit less strict and set to a search backend we know exist...
 216          if (!class_exists($search_type))
 217          {
 218              $search_type = '\phpbb\search\fulltext_native';
 219              $config->set('search_type', $search_type);
 220          }
 221  
 222          if (!class_exists($search_type))
 223          {
 224              trigger_error('NO_SUCH_SEARCH_MODULE');
 225          }
 226  
 227          $error = false;
 228          $convert->fulltext_search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
 229  
 230          if ($error)
 231          {
 232              trigger_error($error);
 233          }
 234  
 235          include_once($phpbb_root_path . 'includes/message_parser.' . $phpEx);
 236          $message_parser = new \parse_message();
 237  
 238          $jump = $request->variable('jump', 0);
 239          $final_jump = $request->variable('final_jump', 0);
 240          $sync_batch = $request->variable('sync_batch', -1);
 241          $last_statement = $request->variable('last', 0);
 242  
 243          // We are running sync...
 244          if ($sync_batch >= 0)
 245          {
 246              $this->sync_forums($converter, $sync_batch);
 247              return;
 248          }
 249  
 250          if ($jump)
 251          {
 252              $this->jump($converter, $jump, $last_statement);
 253              return;
 254          }
 255  
 256          if ($final_jump)
 257          {
 258              $this->final_jump($final_jump);
 259              return;
 260          }
 261  
 262          $current_table = $request->variable('current_table', 0);
 263          $old_current_table = min(-1, $current_table - 1);
 264          $skip_rows = $request->variable('skip_rows', 0);
 265  
 266          if (!$current_table && !$skip_rows)
 267          {
 268              if (!$request->variable('confirm', false))
 269              {
 270                  // If avatars / ranks / smilies folders are specified make sure they are writable
 271                  $bad_folders = array();
 272  
 273                  $local_paths = array(
 274                      'avatar_path'            => path($config['avatar_path']),
 275                      'avatar_gallery_path'    => path($config['avatar_gallery_path']),
 276                      'icons_path'            => path($config['icons_path']),
 277                      'ranks_path'            => path($config['ranks_path']),
 278                      'smilies_path'            => path($config['smilies_path'])
 279                  );
 280  
 281                  foreach ($local_paths as $folder => $local_path)
 282                  {
 283                      if (isset($convert->convertor[$folder]))
 284                      {
 285                          if (empty($convert->convertor['test_file']))
 286                          {
 287                              // test_file is mandantory at the moment so this should never be reached, but just in case...
 288                              $this->error($user->lang['DEV_NO_TEST_FILE'], __LINE__, __FILE__);
 289                          }
 290  
 291                          if (!$local_path || !$this->filesystem->is_writable($phpbb_root_path . $local_path))
 292                          {
 293                              if (!$local_path)
 294                              {
 295                                  $bad_folders[] = sprintf($user->lang['CONFIG_PHPBB_EMPTY'], $folder);
 296                              }
 297                              else
 298                              {
 299                                  $bad_folders[] = $local_path;
 300                              }
 301                          }
 302                      }
 303                  }
 304  
 305                  if (count($bad_folders))
 306                  {
 307                      $msg = (count($bad_folders) == 1) ? $user->lang['MAKE_FOLDER_WRITABLE'] : $user->lang['MAKE_FOLDERS_WRITABLE'];
 308                      sort($bad_folders);
 309                      $this->error(sprintf($msg, implode('<br />', $bad_folders)), __LINE__, __FILE__, true);
 310  
 311                      $this->template->assign_vars(array(
 312                          'L_SUBMIT'    => $user->lang['INSTALL_TEST'],
 313                          'U_ACTION'    => $this->controller_helper->route('phpbb_convert_convert', array('converter' => $converter)),
 314                      ));
 315                      return;
 316                  }
 317  
 318                  // Grab all the tables used in convertor
 319                  $missing_tables = $tables_list = $aliases = array();
 320  
 321                  foreach ($convert->convertor['schema'] as $schema)
 322                  {
 323                      // Skip those not used (because of addons/plugins not detected)
 324                      if (!$schema['target'])
 325                      {
 326                          continue;
 327                      }
 328  
 329                      foreach ($schema as $key => $val)
 330                      {
 331                          // we're dealing with an array like:
 332                          // array('forum_status',            'forums.forum_status',                'is_item_locked')
 333                          if (is_int($key) && !empty($val[1]))
 334                          {
 335                              $temp_data = $val[1];
 336                              if (!is_array($temp_data))
 337                              {
 338                                  $temp_data = array($temp_data);
 339                              }
 340  
 341                              foreach ($temp_data as $value)
 342                              {
 343                                  if (preg_match('/([a-z0-9_]+)\.([a-z0-9_]+)\)* ?A?S? ?([a-z0-9_]*?)\.?([a-z0-9_]*)$/i', $value, $m))
 344                                  {
 345                                      $table = $convert->src_table_prefix . $m[1];
 346                                      $tables_list[$table] = $table;
 347  
 348                                      if (!empty($m[3]))
 349                                      {
 350                                          $aliases[] = $convert->src_table_prefix . $m[3];
 351                                      }
 352                                  }
 353                              }
 354                          }
 355                          // 'left_join'        => 'topics LEFT JOIN vote_desc ON topics.topic_id = vote_desc.topic_id AND topics.topic_vote = 1'
 356                          else if ($key == 'left_join')
 357                          {
 358                              // Convert the value if it wasn't an array already.
 359                              if (!is_array($val))
 360                              {
 361                                  $val = array($val);
 362                              }
 363  
 364                              for ($j = 0, $size = count($val); $j < $size; ++$j)
 365                              {
 366                                  if (preg_match('/LEFT JOIN ([a-z0-9_]+) AS ([a-z0-9_]+)/i', $val[$j], $m))
 367                                  {
 368                                      $table = $convert->src_table_prefix . $m[1];
 369                                      $tables_list[$table] = $table;
 370  
 371                                      if (!empty($m[2]))
 372                                      {
 373                                          $aliases[] = $convert->src_table_prefix . $m[2];
 374                                      }
 375                                  }
 376                              }
 377                          }
 378                      }
 379                  }
 380  
 381                  // Remove aliased tables from $tables_list
 382                  foreach ($aliases as $alias)
 383                  {
 384                      unset($tables_list[$alias]);
 385                  }
 386  
 387                  // Check if the tables that we need exist
 388                  $src_db->sql_return_on_error(true);
 389                  foreach ($tables_list as $table => $null)
 390                  {
 391                      $sql = 'SELECT 1 FROM ' . $table;
 392                      $_result = $src_db->sql_query_limit($sql, 1);
 393  
 394                      if (!$_result)
 395                      {
 396                          $missing_tables[] = $table;
 397                      }
 398                      $src_db->sql_freeresult($_result);
 399                  }
 400                  $src_db->sql_return_on_error(false);
 401  
 402                  // Throw an error if some tables are missing
 403                  // We used to do some guessing here, but since we have a suggestion of possible values earlier, I don't see it adding anything here to do it again
 404  
 405                  if (count($missing_tables) == count($tables_list))
 406                  {
 407                      $this->error($user->lang['NO_TABLES_FOUND'] . ' ' . $user->lang['CHECK_TABLE_PREFIX'], __LINE__, __FILE__);
 408                  }
 409                  else if (count($missing_tables))
 410                  {
 411                      $this->error(sprintf($user->lang['TABLES_MISSING'], implode($user->lang['COMMA_SEPARATOR'], $missing_tables)) . '<br /><br />' . $user->lang['CHECK_TABLE_PREFIX'], __LINE__, __FILE__);
 412                  }
 413  
 414                  $url = $this->save_convert_progress($converter, 'confirm=1');
 415                  $msg = $user->lang['PRE_CONVERT_COMPLETE'];
 416  
 417                  if ($convert->convertor_data['author_notes'])
 418                  {
 419                      $msg .= '</p><p>' . sprintf($user->lang['AUTHOR_NOTES'], $convert->convertor_data['author_notes']);
 420                  }
 421  
 422                  $this->template->assign_vars(array(
 423                      'L_SUBMIT'        => $user->lang['CONTINUE_CONVERT'],
 424                      'BODY'            => $msg,
 425                      'U_ACTION'        => $url,
 426                  ));
 427  
 428                  return;
 429              } // if (!$request->variable('confirm', false)))
 430  
 431              $this->template->assign_block_vars('checks', array(
 432                  'S_LEGEND'        => true,
 433                  'LEGEND'        => $user->lang['STARTING_CONVERT'],
 434              ));
 435  
 436              // Convert the config table and load the settings of the old board
 437              if (!empty($convert->config_schema))
 438              {
 439                  restore_config($convert->config_schema);
 440  
 441                  // Override a couple of config variables for the duration
 442                  $config['max_quote_depth'] = 0;
 443  
 444                  // @todo Need to confirm that max post length in source is <= max post length in destination or there may be interesting formatting issues
 445                  $config['max_post_chars'] = $config['min_post_chars'] = 0;
 446              }
 447  
 448              $this->template->assign_block_vars('checks', array(
 449                  'TITLE'        => $user->lang['CONFIG_CONVERT'],
 450                  'RESULT'    => $user->lang['DONE'],
 451              ));
 452  
 453              // Now process queries and execute functions that have to be executed prior to the conversion
 454              if (!empty($convert->convertor['execute_first']))
 455              {
 456                  // @codingStandardsIgnoreStart
 457                  eval($convert->convertor['execute_first']);
 458                  // @codingStandardsIgnoreEnd
 459              }
 460  
 461              if (!empty($convert->convertor['query_first']))
 462              {
 463                  if (!is_array($convert->convertor['query_first']))
 464                  {
 465                      $convert->convertor['query_first'] = array('target', array($convert->convertor['query_first']));
 466                  }
 467                  else if (!is_array($convert->convertor['query_first'][0]))
 468                  {
 469                      $convert->convertor['query_first'] = array(array($convert->convertor['query_first'][0], $convert->convertor['query_first'][1]));
 470                  }
 471  
 472                  foreach ($convert->convertor['query_first'] as $query_first)
 473                  {
 474                      if ($query_first[0] == 'src')
 475                      {
 476                          if ($convert->mysql_convert && $same_db)
 477                          {
 478                              $src_db->sql_query("SET NAMES 'binary'");
 479                          }
 480  
 481                          $src_db->sql_query($query_first[1]);
 482  
 483                          if ($convert->mysql_convert && $same_db)
 484                          {
 485                              $src_db->sql_query("SET NAMES 'utf8'");
 486                          }
 487                      }
 488                      else
 489                      {
 490                          $db->sql_query($query_first[1]);
 491                      }
 492                  }
 493              }
 494  
 495              $this->template->assign_block_vars('checks', array(
 496                  'TITLE'        => $user->lang['PREPROCESS_STEP'],
 497                  'RESULT'    => $user->lang['DONE'],
 498              ));
 499          } // if (!$current_table && !$skip_rows)
 500  
 501          $this->template->assign_block_vars('checks', array(
 502              'S_LEGEND'        => true,
 503              'LEGEND'        => $user->lang['FILLING_TABLES'],
 504          ));
 505  
 506          // This loop takes one target table and processes it
 507          while ($current_table < count($convert->convertor['schema']))
 508          {
 509              $schema = $convert->convertor['schema'][$current_table];
 510  
 511              // The target table isn't set, this can be because a module (for example the attachement mod) is taking care of this.
 512              if (empty($schema['target']))
 513              {
 514                  $current_table++;
 515                  continue;
 516              }
 517  
 518              $this->template->assign_block_vars('checks', array(
 519                  'TITLE'    => sprintf($user->lang['FILLING_TABLE'], $schema['target']),
 520              ));
 521  
 522              // This is only the case when we first start working on the tables.
 523              if (!$skip_rows)
 524              {
 525                  // process execute_first and query_first for this table...
 526                  if (!empty($schema['execute_first']))
 527                  {
 528                      // @codingStandardsIgnoreStart
 529                      eval($schema['execute_first']);
 530                      // @codingStandardsIgnoreEnd
 531                  }
 532  
 533                  if (!empty($schema['query_first']))
 534                  {
 535                      if (!is_array($schema['query_first']))
 536                      {
 537                          $schema['query_first'] = array('target', array($schema['query_first']));
 538                      }
 539                      else if (!is_array($schema['query_first'][0]))
 540                      {
 541                          $schema['query_first'] = array(array($schema['query_first'][0], $schema['query_first'][1]));
 542                      }
 543  
 544                      foreach ($schema['query_first'] as $query_first)
 545                      {
 546                          if ($query_first[0] == 'src')
 547                          {
 548                              if ($convert->mysql_convert && $same_db)
 549                              {
 550                                  $src_db->sql_query("SET NAMES 'binary'");
 551                              }
 552                              $src_db->sql_query($query_first[1]);
 553                              if ($convert->mysql_convert && $same_db)
 554                              {
 555                                  $src_db->sql_query("SET NAMES 'utf8'");
 556                              }
 557                          }
 558                          else
 559                          {
 560                              $db->sql_query($query_first[1]);
 561                          }
 562                      }
 563                  }
 564  
 565                  if (!empty($schema['autoincrement']))
 566                  {
 567                      switch ($db->get_sql_layer())
 568                      {
 569                          case 'postgres':
 570                              $db->sql_query("SELECT SETVAL('" . $schema['target'] . "_seq',(select case when max(" . $schema['autoincrement'] . ")>0 then max(" . $schema['autoincrement'] . ")+1 else 1 end from " . $schema['target'] . '));');
 571                              break;
 572  
 573                          case 'oracle':
 574                              $result = $db->sql_query('SELECT MAX(' . $schema['autoincrement'] . ') as max_id FROM ' . $schema['target']);
 575                              $row = $db->sql_fetchrow($result);
 576                              $db->sql_freeresult($result);
 577  
 578                              $largest_id = (int) $row['max_id'];
 579  
 580                              if ($largest_id)
 581                              {
 582                                  $db->sql_query('DROP SEQUENCE ' . $schema['target'] . '_seq');
 583                                  $db->sql_query('CREATE SEQUENCE ' . $schema['target'] . '_seq START WITH ' . ($largest_id + 1));
 584                              }
 585                              break;
 586                      }
 587                  }
 588              }
 589  
 590              // Process execute_always for this table
 591              // This is for code which needs to be executed on every pass of this table if
 592              // it gets split because of time restrictions
 593              if (!empty($schema['execute_always']))
 594              {
 595                  // @codingStandardsIgnoreStart
 596                  eval($schema['execute_always']);
 597                  // @codingStandardsIgnoreEnd
 598              }
 599  
 600              //
 601              // Set up some variables
 602              //
 603              // $waiting_rows    holds rows for multirows insertion (MySQL only)
 604              // $src_tables        holds unique tables with aliases to select from
 605              // $src_fields        will quickly refer source fields (or aliases) corresponding to the current index
 606              // $select_fields    holds the names of the fields to retrieve
 607              //
 608  
 609              $sql_data = array(
 610                  'source_fields'        => array(),
 611                  'target_fields'        => array(),
 612                  'source_tables'        => array(),
 613                  'select_fields'        => array(),
 614              );
 615  
 616              // This statement is building the keys for later insertion.
 617              $insert_query = $this->build_insert_query($schema, $sql_data, $current_table);
 618  
 619              // If no source table is affected, we skip the table
 620              if (empty($sql_data['source_tables']))
 621              {
 622                  $skip_rows = 0;
 623                  $current_table++;
 624                  continue;
 625              }
 626  
 627              $distinct = (!empty($schema['distinct'])) ? 'DISTINCT ' : '';
 628  
 629              $sql = 'SELECT ' . $distinct . implode(', ', $sql_data['select_fields']) . " \nFROM " . implode(', ', $sql_data['source_tables']);
 630  
 631              // Where
 632              $sql .= (!empty($schema['where'])) ? "\nWHERE (" . $schema['where'] . ')' : '';
 633  
 634              // Group By
 635              if (!empty($schema['group_by']))
 636              {
 637                  $schema['group_by'] = array($schema['group_by']);
 638                  foreach ($sql_data['select_fields'] as $select)
 639                  {
 640                      $alias = strpos(strtolower($select), ' as ');
 641                      $select = ($alias) ? substr($select, 0, $alias) : $select;
 642                      if (!in_array($select, $schema['group_by']))
 643                      {
 644                          $schema['group_by'][] = $select;
 645                      }
 646                  }
 647              }
 648              $sql .= (!empty($schema['group_by'])) ? "\nGROUP BY " . implode(', ', $schema['group_by']) : '';
 649  
 650              // Having
 651              $sql .= (!empty($schema['having'])) ? "\nHAVING " . $schema['having'] : '';
 652  
 653              // Order By
 654              if (empty($schema['order_by']) && !empty($schema['primary']))
 655              {
 656                  $schema['order_by'] = $schema['primary'];
 657              }
 658              $sql .= (!empty($schema['order_by'])) ? "\nORDER BY " . $schema['order_by'] : '';
 659  
 660              // Counting basically holds the amount of rows processed.
 661              $counting = -1;
 662              $batch_time = 0;
 663  
 664              while ($counting === -1 || ($counting >= $convert->batch_size && still_on_time()))
 665              {
 666                  $old_current_table = $current_table;
 667  
 668                  $rows = '';
 669                  $waiting_rows = array();
 670  
 671                  if (!empty($batch_time))
 672                  {
 673                      $mtime = explode(' ', microtime());
 674                      $mtime = $mtime[0] + $mtime[1];
 675                      $rows = ceil($counting/($mtime - $batch_time)) . " rows/s ($counting rows) | ";
 676                  }
 677  
 678                  $this->template->assign_block_vars('checks', array(
 679                      'TITLE'        => "skip_rows = $skip_rows",
 680                      'RESULT'    => $rows . (($phpbb_container->getParameter('debug.memory') && function_exists('memory_get_usage')) ? ceil(memory_get_usage()/1024) . ' ' . $user->lang['KIB'] : ''),
 681                  ));
 682  
 683                  $mtime = explode(' ', microtime());
 684                  $batch_time = $mtime[0] + $mtime[1];
 685  
 686                  if ($convert->mysql_convert && $same_db)
 687                  {
 688                      $src_db->sql_query("SET NAMES 'binary'");
 689                  }
 690  
 691                  // Take skip rows into account and only fetch batch_size amount of rows
 692                  $___result = $src_db->sql_query_limit($sql, $convert->batch_size, $skip_rows);
 693  
 694                  if ($convert->mysql_convert && $same_db)
 695                  {
 696                      $src_db->sql_query("SET NAMES 'utf8'");
 697                  }
 698  
 699                  // This loop processes each row
 700                  $counting = 0;
 701  
 702                  $convert->row = $convert_row = array();
 703  
 704                  if (!empty($schema['autoincrement']))
 705                  {
 706                      switch ($db->get_sql_layer())
 707                      {
 708                          case 'mssql_odbc':
 709                          case 'mssqlnative':
 710                              $db->sql_query('SET IDENTITY_INSERT ' . $schema['target'] . ' ON');
 711                              break;
 712                      }
 713                  }
 714  
 715                  // Now handle the rows until time is over or no more rows to process...
 716                  while ($counting === 0 || still_on_time())
 717                  {
 718                      $convert_row = $src_db->sql_fetchrow($___result);
 719  
 720                      if (!$convert_row)
 721                      {
 722                          // move to the next batch or table
 723                          break;
 724                      }
 725  
 726                      // With this we are able to always save the last state
 727                      $convert->row = $convert_row;
 728  
 729                      // Increment the counting variable, it stores the number of rows we have processed
 730                      $counting++;
 731  
 732                      $insert_values = array();
 733  
 734                      $sql_flag = $this->process_row($schema, $sql_data, $insert_values);
 735  
 736                      if ($sql_flag === true)
 737                      {
 738                          switch ($db->get_sql_layer())
 739                          {
 740                              // If MySQL, we'll wait to have num_wait_rows rows to submit at once
 741                              case 'mysqli':
 742                                  $waiting_rows[] = '(' . implode(', ', $insert_values) . ')';
 743  
 744                                  if (count($waiting_rows) >= $convert->num_wait_rows)
 745                                  {
 746                                      $errored = false;
 747  
 748                                      $db->sql_return_on_error(true);
 749  
 750                                      if (!$db->sql_query($insert_query . implode(', ', $waiting_rows)))
 751                                      {
 752                                          $errored = true;
 753                                      }
 754                                      $db->sql_return_on_error(false);
 755  
 756                                      if ($errored)
 757                                      {
 758                                          $db->sql_return_on_error(true);
 759  
 760                                          // Because it errored out we will try to insert the rows one by one... most of the time this
 761                                          // is caused by duplicate entries - but we also do not want to miss one...
 762                                          foreach ($waiting_rows as $waiting_sql)
 763                                          {
 764                                              if (!$db->sql_query($insert_query . $waiting_sql))
 765                                              {
 766                                                  $this->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_query . $waiting_sql, ENT_COMPAT) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true), ENT_COMPAT), __LINE__, __FILE__, true);
 767                                              }
 768                                          }
 769  
 770                                          $db->sql_return_on_error(false);
 771                                      }
 772  
 773                                      $waiting_rows = array();
 774                                  }
 775  
 776                                  break;
 777  
 778                              default:
 779                                  $insert_sql = $insert_query . '(' . implode(', ', $insert_values) . ')';
 780  
 781                                  $db->sql_return_on_error(true);
 782  
 783                                  if (!$db->sql_query($insert_sql))
 784                                  {
 785                                      $this->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_sql, ENT_COMPAT) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true), ENT_COMPAT), __LINE__, __FILE__, true);
 786                                  }
 787                                  $db->sql_return_on_error(false);
 788  
 789                                  $waiting_rows = array();
 790  
 791                                  break;
 792                          }
 793                      }
 794  
 795                      $skip_rows++;
 796                  }
 797                  $src_db->sql_freeresult($___result);
 798  
 799                  // We might still have some rows waiting
 800                  if (count($waiting_rows))
 801                  {
 802                      $errored = false;
 803                      $db->sql_return_on_error(true);
 804  
 805                      if (!$db->sql_query($insert_query . implode(', ', $waiting_rows)))
 806                      {
 807                          $errored = true;
 808                      }
 809                      $db->sql_return_on_error(false);
 810  
 811                      if ($errored)
 812                      {
 813                          $db->sql_return_on_error(true);
 814  
 815                          // Because it errored out we will try to insert the rows one by one... most of the time this
 816                          // is caused by duplicate entries - but we also do not want to miss one...
 817                          foreach ($waiting_rows as $waiting_sql)
 818                          {
 819                              $db->sql_query($insert_query . $waiting_sql);
 820                              $this->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_query . $waiting_sql, ENT_COMPAT) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true), ENT_COMPAT), __LINE__, __FILE__, true);
 821                          }
 822  
 823                          $db->sql_return_on_error(false);
 824                      }
 825  
 826                      $waiting_rows = array();
 827                  }
 828  
 829                  if (!empty($schema['autoincrement']))
 830                  {
 831                      switch ($db->get_sql_layer())
 832                      {
 833                          case 'mssql_odbc':
 834                          case 'mssqlnative':
 835                              $db->sql_query('SET IDENTITY_INSERT ' . $schema['target'] . ' OFF');
 836                              break;
 837  
 838                          case 'postgres':
 839                              $db->sql_query("SELECT SETVAL('" . $schema['target'] . "_seq',(select case when max(" . $schema['autoincrement'] . ")>0 then max(" . $schema['autoincrement'] . ")+1 else 1 end from " . $schema['target'] . '));');
 840                              break;
 841  
 842                          case 'oracle':
 843                              $result = $db->sql_query('SELECT MAX(' . $schema['autoincrement'] . ') as max_id FROM ' . $schema['target']);
 844                              $row = $db->sql_fetchrow($result);
 845                              $db->sql_freeresult($result);
 846  
 847                              $largest_id = (int) $row['max_id'];
 848  
 849                              if ($largest_id)
 850                              {
 851                                  $db->sql_query('DROP SEQUENCE ' . $schema['target'] . '_seq');
 852                                  $db->sql_query('CREATE SEQUENCE ' . $schema['target'] . '_seq START WITH ' . ($largest_id + 1));
 853                              }
 854                              break;
 855                      }
 856                  }
 857              }
 858  
 859              // When we reach this point, either the current table has been processed or we're running out of time.
 860              if (still_on_time() && $counting < $convert->batch_size/* && !defined('DEBUG')*/)
 861              {
 862                  $skip_rows = 0;
 863                  $current_table++;
 864              }
 865              else
 866              {/*
 867                  if (still_on_time() && $counting < $convert->batch_size)
 868                  {
 869                      $skip_rows = 0;
 870                      $current_table++;
 871                  }*/
 872  
 873                  // Looks like we ran out of time.
 874                  $url = $this->save_convert_progress($converter, 'current_table=' . $current_table . '&amp;skip_rows=' . $skip_rows);
 875  
 876                  $current_table++;
 877  //                $percentage = ($skip_rows == 0) ? 0 : floor(100 / ($total_rows / $skip_rows));
 878  
 879                  $msg = sprintf($user->lang['STEP_PERCENT_COMPLETED'], $current_table, count($convert->convertor['schema']));
 880  
 881                  $this->template->assign_vars(array(
 882                      'BODY'            => $msg,
 883                      'L_SUBMIT'        => $user->lang['CONTINUE_CONVERT'],
 884                      'U_ACTION'        => $url,
 885                  ));
 886  
 887                  $this->meta_refresh($url);
 888                  return;
 889              }
 890          }
 891  
 892          // Process execute_last then we'll be done
 893          $url = $this->save_convert_progress($converter, 'jump=1');
 894  
 895          $this->template->assign_vars(array(
 896              'L_SUBMIT'        => $user->lang['FINAL_STEP'],
 897              'U_ACTION'        => $url,
 898          ));
 899  
 900          $this->meta_refresh($url);
 901          return;
 902      }
 903  
 904      /**
 905       * Sync function being executed at the middle, some functions need to be executed after a successful sync.
 906       */
 907  	function sync_forums($converter, $sync_batch)
 908      {
 909          global $user, $db, $phpbb_root_path, $phpEx, $config, $cache;
 910          global $convert;
 911          global $phpbb_container;
 912  
 913          include_once ($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
 914  
 915          $this->template->assign_block_vars('checks', array(
 916              'S_LEGEND'    => true,
 917              'LEGEND'    => $user->lang['SYNC_TOPICS'],
 918          ));
 919  
 920          $batch_size = $convert->batch_size;
 921  
 922          $sql = 'SELECT MIN(topic_id) as min_value, MAX(topic_id) AS max_value
 923              FROM ' . TOPICS_TABLE;
 924          $result = $db->sql_query($sql);
 925          $row = $db->sql_fetchrow($result);
 926          $db->sql_freeresult($result);
 927  
 928          // Set values of minimum/maximum primary value for this table.
 929          $primary_min = $row['min_value'];
 930          $primary_max = $row['max_value'];
 931  
 932          if ($sync_batch == 0)
 933          {
 934              $sync_batch = (int) $primary_min;
 935          }
 936  
 937          if ($sync_batch == 0)
 938          {
 939              $sync_batch = 1;
 940          }
 941  
 942          // Fetch a batch of rows, process and insert them.
 943          while ($sync_batch <= $primary_max && still_on_time())
 944          {
 945              $end = ($sync_batch + $batch_size - 1);
 946  
 947              // Sync all topics in batch mode...
 948              sync('topic', 'range', 'topic_id BETWEEN ' . $sync_batch . ' AND ' . $end, true, true);
 949  
 950              $this->template->assign_block_vars('checks', array(
 951                  'TITLE'        => sprintf($user->lang['SYNC_TOPIC_ID'], $sync_batch, ($sync_batch + $batch_size)) . (($phpbb_container->getParameter('debug.memory') && function_exists('memory_get_usage')) ? ' [' . ceil(memory_get_usage()/1024) . ' ' . $user->lang['KIB'] . ']' : ''),
 952                  'RESULT'    => $user->lang['DONE'],
 953              ));
 954  
 955              $sync_batch += $batch_size;
 956          }
 957  
 958          if ($sync_batch >= $primary_max)
 959          {
 960              $url = $this->save_convert_progress($converter, 'final_jump=1');
 961  
 962              $this->template->assign_vars(array(
 963                  'L_SUBMIT'        => $user->lang['CONTINUE_CONVERT'],
 964                  'U_ACTION'        => $url,
 965              ));
 966  
 967              $this->meta_refresh($url);
 968              return;
 969          }
 970          else
 971          {
 972              $sync_batch--;
 973          }
 974  
 975          $url = $this->save_convert_progress($converter, 'sync_batch=' . $sync_batch);
 976  
 977          $this->template->assign_vars(array(
 978              'L_SUBMIT'        => $user->lang['CONTINUE_CONVERT'],
 979              'U_ACTION'        => $url,
 980          ));
 981  
 982          $this->meta_refresh($url);
 983          return;
 984      }
 985  
 986      /**
 987       * Save the convertor status
 988       */
 989  	function save_convert_progress($convertor_tag, $step)
 990      {
 991          global $config, $convert, $language;
 992  
 993          // Save convertor Status
 994          $config->set('convert_progress', serialize(array(
 995              'step'            => $step,
 996              'table_prefix'    => $convert->src_table_prefix,
 997              'tag'            => $convert->convertor_tag,
 998          )), false);
 999  
1000          $config->set('convert_db_server', serialize(array(
1001              'dbms'            => $convert->src_dbms,
1002              'dbhost'        => $convert->src_dbhost,
1003              'dbport'        => $convert->src_dbport,
1004              'dbname'        => $convert->src_dbname,
1005          )), false);
1006  
1007          $config->set('convert_db_user', serialize(array(
1008              'dbuser'        => $convert->src_dbuser,
1009              'dbpasswd'        => $convert->src_dbpasswd,
1010          )), false);
1011  
1012          return $this->controller_helper->route('phpbb_convert_convert', array('converter' => $convertor_tag)) . '?' . $step;
1013      }
1014  
1015      /**
1016       * Finish conversion, the last function to be called.
1017       */
1018  	function finish_conversion()
1019      {
1020          global $db, $phpbb_root_path, $phpEx, $convert, $config, $language, $user;
1021          global $cache, $auth, $phpbb_container, $phpbb_log;
1022  
1023          include_once ($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
1024  
1025          $db->sql_query('DELETE FROM ' . CONFIG_TABLE . "
1026              WHERE config_name = 'convert_progress'
1027                  OR config_name = 'convert_options'
1028                  OR config_name = 'convert_db_server'
1029                  OR config_name = 'convert_db_user'");
1030          $db->sql_query('DELETE FROM ' . SESSIONS_TABLE);
1031  
1032          @unlink($phpbb_container->getParameter('core.cache_dir') . 'data_global.' . $phpEx);
1033          phpbb_cache_moderators($db, $cache, $auth);
1034  
1035          // And finally, add a note to the log
1036          $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_INSTALL_CONVERTED', false, array($convert->convertor_data['forum_name'], $config['version']));
1037  
1038          $url = $this->controller_helper->route('phpbb_convert_finish');
1039  
1040          $this->template->assign_vars(array(
1041              'L_SUBMIT'        => $user->lang['FINAL_STEP'],
1042              'U_ACTION'        => $url,
1043          ));
1044  
1045          $this->meta_refresh($url);
1046          return;
1047      }
1048  
1049      /**
1050       * This function marks the steps after syncing
1051       */
1052  	function final_jump($final_jump)
1053      {
1054          global $user, $src_db, $same_db, $db, $phpbb_root_path, $phpEx, $config, $cache;
1055          global $convert;
1056  
1057          $this->template->assign_block_vars('checks', array(
1058              'S_LEGEND'    => true,
1059              'LEGEND'    => $user->lang['PROCESS_LAST'],
1060          ));
1061  
1062          if ($final_jump == 1)
1063          {
1064              $db->sql_return_on_error(true);
1065  
1066              update_topics_posted();
1067  
1068              $this->template->assign_block_vars('checks', array(
1069                  'TITLE'        => $user->lang['UPDATE_TOPICS_POSTED'],
1070                  'RESULT'    => $user->lang['DONE'],
1071              ));
1072  
1073              if ($db->get_sql_error_triggered())
1074              {
1075                  $this->template->assign_vars(array(
1076                      'S_ERROR_BOX'    => true,
1077                      'ERROR_TITLE'    => $user->lang['UPDATE_TOPICS_POSTED'],
1078                      'ERROR_MSG'        => $user->lang['UPDATE_TOPICS_POSTED_ERR'],
1079                  ));
1080              }
1081              $db->sql_return_on_error(false);
1082  
1083              $this->finish_conversion();
1084              return;
1085          }
1086      }
1087  
1088      /**
1089       * This function marks the steps before syncing (jump=1)
1090       */
1091  	function jump($converter, $jump, $last_statement)
1092      {
1093          /** @var \phpbb\db\driver\driver_interface $src_db */
1094          /** @var \phpbb\cache\driver\driver_interface $cache */
1095          global $user, $src_db, $same_db, $db, $phpbb_root_path, $phpEx, $config, $cache;
1096          global $convert;
1097  
1098          include_once ($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
1099  
1100          $this->template->assign_block_vars('checks', array(
1101              'S_LEGEND'    => true,
1102              'LEGEND'    => $user->lang['PROCESS_LAST'],
1103          ));
1104  
1105          if ($jump == 1)
1106          {
1107              // Execute 'last' statements/queries
1108              if (!empty($convert->convertor['execute_last']))
1109              {
1110                  if (!is_array($convert->convertor['execute_last']))
1111                  {
1112                      // @codingStandardsIgnoreStart
1113                      eval($convert->convertor['execute_last']);
1114                      // @codingStandardsIgnoreEnd
1115                  }
1116                  else
1117                  {
1118                      while ($last_statement < count($convert->convertor['execute_last']))
1119                      {
1120                          // @codingStandardsIgnoreStart
1121                          eval($convert->convertor['execute_last'][$last_statement]);
1122                          // @codingStandardsIgnoreEnd
1123  
1124                          $this->template->assign_block_vars('checks', array(
1125                              'TITLE'        => $convert->convertor['execute_last'][$last_statement],
1126                              'RESULT'    => $user->lang['DONE'],
1127                          ));
1128  
1129                          $last_statement++;
1130                          $url = $this->save_convert_progress($converter, 'jump=1&amp;last=' . $last_statement);
1131  
1132                          $percentage = ($last_statement == 0) ? 0 : floor(100 / (count($convert->convertor['execute_last']) / $last_statement));
1133                          $msg = sprintf($user->lang['STEP_PERCENT_COMPLETED'], $last_statement, count($convert->convertor['execute_last']), $percentage);
1134  
1135                          $this->template->assign_vars(array(
1136                              'L_SUBMIT'        => $user->lang['CONTINUE_LAST'],
1137                              'BODY'            => $msg,
1138                              'U_ACTION'        => $url,
1139                          ));
1140  
1141                          $this->meta_refresh($url);
1142                          return;
1143                      }
1144                  }
1145              }
1146  
1147              if (!empty($convert->convertor['query_last']))
1148              {
1149                  if (!is_array($convert->convertor['query_last']))
1150                  {
1151                      $convert->convertor['query_last'] = array('target', array($convert->convertor['query_last']));
1152                  }
1153                  else if (!is_array($convert->convertor['query_last'][0]))
1154                  {
1155                      $convert->convertor['query_last'] = array(array($convert->convertor['query_last'][0], $convert->convertor['query_last'][1]));
1156                  }
1157  
1158                  foreach ($convert->convertor['query_last'] as $query_last)
1159                  {
1160                      if ($query_last[0] == 'src')
1161                      {
1162                          if ($convert->mysql_convert && $same_db)
1163                          {
1164                              $src_db->sql_query("SET NAMES 'binary'");
1165                          }
1166  
1167                          $src_db->sql_query($query_last[1]);
1168  
1169                          if ($convert->mysql_convert && $same_db)
1170                          {
1171                              $src_db->sql_query("SET NAMES 'utf8'");
1172                          }
1173                      }
1174                      else
1175                      {
1176                          $db->sql_query($query_last[1]);
1177                      }
1178                  }
1179              }
1180  
1181              // Sanity check
1182              $db->sql_return_on_error(false);
1183              $src_db->sql_return_on_error(false);
1184  
1185              fix_empty_primary_groups();
1186  
1187              $sql = 'SELECT MIN(user_regdate) AS board_startdate
1188                  FROM ' . USERS_TABLE;
1189              $result = $db->sql_query($sql);
1190              $row = $db->sql_fetchrow($result);
1191              $db->sql_freeresult($result);
1192  
1193              if (!isset($config['board_startdate']) || ($row['board_startdate'] < $config['board_startdate'] && $row['board_startdate'] > 0))
1194              {
1195                  $config->set('board_startdate', $row['board_startdate']);
1196                  $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_regdate = ' . $row['board_startdate'] . ' WHERE user_id = ' . ANONYMOUS);
1197              }
1198  
1199              update_dynamic_config();
1200  
1201              $this->template->assign_block_vars('checks', array(
1202                  'TITLE'        => $user->lang['CLEAN_VERIFY'],
1203                  'RESULT'    => $user->lang['DONE'],
1204              ));
1205  
1206              $url = $this->save_convert_progress($converter, 'jump=2');
1207  
1208              $this->template->assign_vars(array(
1209                  'L_SUBMIT'        => $user->lang['CONTINUE_CONVERT'],
1210                  'U_ACTION'        => $url,
1211              ));
1212  
1213              $this->meta_refresh($url);
1214              return;
1215          }
1216  
1217          if ($jump == 2)
1218          {
1219              $db->sql_query('UPDATE ' . USERS_TABLE . " SET user_permissions = ''");
1220  
1221              // TODO: sync() is likely going to bomb out on forums with a considerable amount of topics.
1222              // TODO: the sync function is able to handle FROM-TO values, we should use them here (batch processing)
1223              sync('forum', '', '', false, true);
1224              $cache->destroy('sql', FORUMS_TABLE);
1225  
1226              $this->template->assign_block_vars('checks', array(
1227                  'TITLE'        => $user->lang['SYNC_FORUMS'],
1228                  'RESULT'    => $user->lang['DONE'],
1229              ));
1230  
1231              // Continue with synchronizing the forums...
1232              $url = $this->save_convert_progress($converter, 'sync_batch=0');
1233  
1234              $this->template->assign_vars(array(
1235                  'L_SUBMIT'        => $user->lang['CONTINUE_CONVERT'],
1236                  'U_ACTION'        => $url,
1237              ));
1238  
1239              $this->meta_refresh($url);
1240              return;
1241          }
1242      }
1243  
1244  	function build_insert_query(&$schema, &$sql_data, $current_table)
1245      {
1246          global $db, $user;
1247          global $convert;
1248  
1249          $insert_query = 'INSERT INTO ' . $schema['target'] . ' (';
1250  
1251          $aliases = array();
1252  
1253          $sql_data = array(
1254              'source_fields'        => array(),
1255              'target_fields'        => array(),
1256              'source_tables'        => array(),
1257              'select_fields'        => array(),
1258          );
1259  
1260          foreach ($schema as $key => $val)
1261          {
1262              // Example: array('group_name',                'extension_groups.group_name',        'htmlspecialchars'),
1263              if (is_int($key))
1264              {
1265                  if (!empty($val[0]))
1266                  {
1267                      // Target fields
1268                      $sql_data['target_fields'][$val[0]] = $key;
1269                      $insert_query .= $val[0] . ', ';
1270                  }
1271  
1272                  if (!is_array($val[1]))
1273                  {
1274                      $val[1] = array($val[1]);
1275                  }
1276  
1277                  foreach ($val[1] as $valkey => $value_1)
1278                  {
1279                      // This should cover about any case:
1280                      //
1281                      // table.field                    => SELECT table.field                FROM table
1282                      // table.field AS alias            => SELECT table.field    AS alias    FROM table
1283                      // table.field AS table2.alias    => SELECT table2.field    AS alias    FROM table table2
1284                      // table.field AS table2.field    => SELECT table2.field                FROM table table2
1285                      //
1286                      if (preg_match('/^([a-z0-9_]+)\.([a-z0-9_]+)( +AS +(([a-z0-9_]+?)\.)?([a-z0-9_]+))?$/i', $value_1, $m))
1287                      {
1288                          // There is 'AS ...' in the field names
1289                          if (!empty($m[3]))
1290                          {
1291                              $value_1 = ($m[2] == $m[6]) ? $m[1] . '.' . $m[2] : $m[1] . '.' . $m[2] . ' AS ' . $m[6];
1292  
1293                              // Table alias: store it then replace the source table with it
1294                              if (!empty($m[5]) && $m[5] != $m[1])
1295                              {
1296                                  $aliases[$m[5]] = $m[1];
1297                                  $value_1 = str_replace($m[1] . '.' . $m[2], $m[5] . '.' . $m[2], $value_1);
1298                              }
1299                          }
1300                          else
1301                          {
1302                              // No table alias
1303                              $sql_data['source_tables'][$m[1]] = (empty($convert->src_table_prefix)) ? $m[1] : $convert->src_table_prefix . $m[1] . ' ' . $db->sql_quote($m[1]);
1304                          }
1305  
1306                          $sql_data['select_fields'][$value_1] = $value_1;
1307                          $sql_data['source_fields'][$key][$valkey] = (!empty($m[6])) ? $m[6] : $m[2];
1308                      }
1309                  }
1310              }
1311              else if ($key == 'where' || $key == 'group_by' || $key == 'order_by' || $key == 'having')
1312              {
1313                  if (@preg_match_all('/([a-z0-9_]+)\.([a-z0-9_]+)/i', $val, $m))
1314                  {
1315                      foreach ($m[1] as $value)
1316                      {
1317                          $sql_data['source_tables'][$value] = (empty($convert->src_table_prefix)) ? $value : $convert->src_table_prefix . $value . ' ' . $db->sql_quote($value);
1318                      }
1319                  }
1320              }
1321          }
1322  
1323          // Add the aliases to the list of tables
1324          foreach ($aliases as $alias => $table)
1325          {
1326              $sql_data['source_tables'][$alias] = $convert->src_table_prefix . $table . ' ' . $db->sql_quote($alias);
1327          }
1328  
1329          // 'left_join'        => 'forums LEFT JOIN forum_prune ON forums.forum_id = forum_prune.forum_id',
1330          if (!empty($schema['left_join']))
1331          {
1332              if (!is_array($schema['left_join']))
1333              {
1334                  $schema['left_join'] = array($schema['left_join']);
1335              }
1336  
1337              foreach ($schema['left_join'] as $left_join)
1338              {
1339                  // This won't handle concatened LEFT JOINs
1340                  if (!preg_match('/([a-z0-9_]+) LEFT JOIN ([a-z0-9_]+) A?S? ?([a-z0-9_]*?) ?(ON|USING)(.*)/i', $left_join, $m))
1341                  {
1342                      $this->error(sprintf($user->lang['NOT_UNDERSTAND'], 'LEFT JOIN', $left_join, $current_table, $schema['target']), __LINE__, __FILE__);
1343                  }
1344  
1345                  if (!empty($aliases[$m[2]]))
1346                  {
1347                      if (!empty($m[3]))
1348                      {
1349                          $this->error(sprintf($user->lang['NAMING_CONFLICT'], $m[2], $m[3], $schema['left_join']), __LINE__, __FILE__);
1350                      }
1351  
1352                      $m[2] = $aliases[$m[2]];
1353                      $m[3] = $m[2];
1354                  }
1355  
1356                  $right_table = $convert->src_table_prefix . $m[2];
1357                  if (!empty($m[3]))
1358                  {
1359                      unset($sql_data['source_tables'][$m[3]]);
1360                  }
1361                  else if ($m[2] != $m[1])
1362                  {
1363                      unset($sql_data['source_tables'][$m[2]]);
1364                  }
1365  
1366                  if (strpos($sql_data['source_tables'][$m[1]], "\nLEFT JOIN") !== false)
1367                  {
1368                      $sql_data['source_tables'][$m[1]] = '(' . $sql_data['source_tables'][$m[1]] . ")\nLEFT JOIN $right_table";
1369                  }
1370                  else
1371                  {
1372                      $sql_data['source_tables'][$m[1]] .= "\nLEFT JOIN $right_table";
1373                  }
1374  
1375                  if (!empty($m[3]))
1376                  {
1377                      unset($sql_data['source_tables'][$m[3]]);
1378                      $sql_data['source_tables'][$m[1]] .= ' AS ' . $m[3];
1379                  }
1380                  else if (!empty($convert->src_table_prefix))
1381                  {
1382                      $sql_data['source_tables'][$m[1]] .= ' AS ' . $m[2];
1383                  }
1384                  $sql_data['source_tables'][$m[1]] .= ' ' . $m[4] . $m[5];
1385              }
1386          }
1387  
1388          // Remove ", " from the end of the insert query
1389          $insert_query = substr($insert_query, 0, -2) . ') VALUES ';
1390  
1391          return $insert_query;
1392      }
1393  
1394      /**
1395       * Function for processing the currently handled row
1396       */
1397  	function process_row(&$schema, &$sql_data, &$insert_values)
1398      {
1399          global $user, $phpbb_root_path, $phpEx, $db, $lang, $config, $cache;
1400          global $convert, $convert_row;
1401  
1402          $sql_flag = false;
1403  
1404          foreach ($schema as $key => $fields)
1405          {
1406              // We are only interested in the lines with:
1407              // array('comment', 'attachments_desc.comment', 'htmlspecialchars'),
1408              if (is_int($key))
1409              {
1410                  if (!is_array($fields[1]))
1411                  {
1412                      $fields[1] = array($fields[1]);
1413                  }
1414  
1415                  $firstkey_set = false;
1416                  $firstkey = 0;
1417  
1418                  foreach ($fields[1] as $inner_key => $inner_value)
1419                  {
1420                      if (!$firstkey_set)
1421                      {
1422                          $firstkey = $inner_key;
1423                          $firstkey_set = true;
1424                      }
1425  
1426                      $src_field = isset($sql_data['source_fields'][$key][$inner_key]) ? $sql_data['source_fields'][$key][$inner_key] : '';
1427  
1428                      if (!empty($src_field))
1429                      {
1430                          $fields[1][$inner_key] = $convert->row[$src_field];
1431                      }
1432                  }
1433  
1434                  if (!empty($fields[0]))
1435                  {
1436                      // We have a target field, if we haven't set $sql_flag yet it will be set to TRUE.
1437                      // If a function has already set it to FALSE it won't change it.
1438                      if ($sql_flag === false)
1439                      {
1440                          $sql_flag = true;
1441                      }
1442  
1443                      // No function assigned?
1444                      if (empty($fields[2]))
1445                      {
1446                          $value = $fields[1][$firstkey];
1447                      }
1448                      else if (is_array($fields[2]) && !is_callable($fields[2]))
1449                      {
1450                          // Execute complex function/eval/typecast
1451                          $value = $fields[1];
1452  
1453                          foreach ($fields[2] as $type => $execution)
1454                          {
1455                              if (strpos($type, 'typecast') === 0)
1456                              {
1457                                  if (!is_array($value))
1458                                  {
1459                                      $value = array($value);
1460                                  }
1461                                  $value = $value[0];
1462                                  settype($value, $execution);
1463                              }
1464                              else if (strpos($type, 'function') === 0)
1465                              {
1466                                  if (!is_array($value))
1467                                  {
1468                                      $value = array($value);
1469                                  }
1470  
1471                                  // Add ENT_COMPAT default flag to html specialchars/entities functions, see PHPBB3-16690
1472                                  if (in_array($execution, ['htmlspecialchars', 'htmlentities', 'htmlspecialchars_decode', 'html_entitity_decode']))
1473                                  {
1474                                      $value[] = ENT_COMPAT;
1475                                  }
1476  
1477                                  $value = call_user_func_array($execution, $value);
1478                              }
1479                              else if (strpos($type, 'execute') === 0)
1480                              {
1481                                  if (!is_array($value))
1482                                  {
1483                                      $value = array($value);
1484                                  }
1485  
1486                                  $execution = str_replace('{RESULT}', '$value', $execution);
1487                                  $execution = str_replace('{VALUE}', '$value', $execution);
1488                                  // @codingStandardsIgnoreStart
1489                                  eval($execution);
1490                                  // @codingStandardsIgnoreEnd
1491                              }
1492                          }
1493                      }
1494                      else
1495                      {
1496                          $value = call_user_func_array($fields[2], $fields[1]);
1497                      }
1498  
1499                      if (is_null($value))
1500                      {
1501                          $value = '';
1502                      }
1503  
1504                      $insert_values[] = $db->_sql_validate_value($value);
1505                  }
1506                  else if (!empty($fields[2]))
1507                  {
1508                      if (is_array($fields[2]))
1509                      {
1510                          // Execute complex function/eval/typecast
1511                          $value = '';
1512  
1513                          foreach ($fields[2] as $type => $execution)
1514                          {
1515                              if (strpos($type, 'typecast') === 0)
1516                              {
1517                                  $value = settype($value, $execution);
1518                              }
1519                              else if (strpos($type, 'function') === 0)
1520                              {
1521                                  if (!is_array($value))
1522                                  {
1523                                      $value = array($value);
1524                                  }
1525  
1526                                  // Add ENT_COMPAT default flag to html specialchars/entities functions, see PHPBB3-16690
1527                                  if (in_array($execution, ['htmlspecialchars', 'htmlentities', 'htmlspecialchars_decode', 'html_entitity_decode']))
1528                                  {
1529                                      $value[] = ENT_COMPAT;
1530                                  }
1531  
1532                                  $value = call_user_func_array($execution, $value);
1533                              }
1534                              else if (strpos($type, 'execute') === 0)
1535                              {
1536                                  if (!is_array($value))
1537                                  {
1538                                      $value = array($value);
1539                                  }
1540  
1541                                  $execution = str_replace('{RESULT}', '$value', $execution);
1542                                  $execution = str_replace('{VALUE}', '$value', $execution);
1543                                  // @codingStandardsIgnoreStart
1544                                  eval($execution);
1545                                  // @codingStandardsIgnoreEnd
1546                              }
1547                          }
1548                      }
1549                      else
1550                      {
1551                          call_user_func_array($fields[2], $fields[1]);
1552                      }
1553                  }
1554              }
1555          }
1556  
1557          return $sql_flag;
1558      }
1559  
1560      /**
1561       * Own meta refresh function to be able to change the global time used
1562       */
1563  	function meta_refresh($url)
1564      {
1565          global $convert;
1566  
1567          if ($convert->options['refresh'])
1568          {
1569              // Because we should not rely on correct settings, we simply use the relative path here directly.
1570              $this->template->assign_vars(array(
1571                      'S_REFRESH'    => true,
1572                      'META'        => '<meta http-equiv="refresh" content="5; url=' . $url . '" />')
1573              );
1574          }
1575      }
1576  
1577      /**
1578       * Error handler function
1579       *
1580       * This function needs to be kept for BC
1581       *
1582       * @param $error
1583       * @param $line
1584       * @param $file
1585       * @param bool|false $skip
1586       */
1587  	public function error($error, $line, $file, $skip = false)
1588      {
1589          $this->template->assign_block_vars('errors', array(
1590              'TITLE'    => $error,
1591              'DESCRIPTION' => 'In ' . $file . ' on line ' . $line,
1592          ));
1593      }
1594  
1595      /**
1596       * Database error handler function
1597       *
1598       * This function needs to be kept for BC
1599       *
1600       * @param $error
1601       * @param $sql
1602       * @param $line
1603       * @param $file
1604       * @param bool|false $skip
1605       */
1606  	public function db_error($error, $sql, $line, $file, $skip = false)
1607      {
1608          $this->template->assign_block_vars('errors', array(
1609              'TITLE'    => $error,
1610              'DESCRIPTION' => 'In ' . $file . ' on line ' . $line . '<br /><br /><strong>SQL:</strong> ' . $sql,
1611          ));
1612      }
1613  }


Generated: Sun Jun 23 12:25:44 2024 Cross-referenced by PHPXref 0.7.1