[ Index ]

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


Generated: Tue Apr 7 19:42:26 2020 Cross-referenced by PHPXref 0.7.1