[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
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 . '&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&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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |