Edit File by line
/home/barbar84/www/wp-conte.../plugins/updraftp.../includes
File: class-search-replace.php
<?php
[0] Fix | Delete
[1] Fix | Delete
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
[2] Fix | Delete
[3] Fix | Delete
class UpdraftPlus_Search_Replace {
[4] Fix | Delete
[5] Fix | Delete
private $known_incomplete_classes = array();
[6] Fix | Delete
private $columns = array();
[7] Fix | Delete
private $current_row = 0;
[8] Fix | Delete
[9] Fix | Delete
private $use_wpdb = false;
[10] Fix | Delete
private $use_mysqli = false;
[11] Fix | Delete
private $wpdb_obj = null;
[12] Fix | Delete
private $mysql_dbh = null;
[13] Fix | Delete
[14] Fix | Delete
/**
[15] Fix | Delete
* Constructor
[16] Fix | Delete
*/
[17] Fix | Delete
public function __construct() {
[18] Fix | Delete
add_action('updraftplus_restore_db_pre', array($this, 'updraftplus_restore_db_pre'));
[19] Fix | Delete
}
[20] Fix | Delete
[21] Fix | Delete
/**
[22] Fix | Delete
* This function is called via the filter updraftplus_restore_db_pre it sets up the search and replace database objects
[23] Fix | Delete
*
[24] Fix | Delete
* @return void
[25] Fix | Delete
*/
[26] Fix | Delete
public function updraftplus_restore_db_pre() {
[27] Fix | Delete
global $wpdb, $updraftplus_restorer;
[28] Fix | Delete
[29] Fix | Delete
$this->use_wpdb = $updraftplus_restorer->use_wpdb();
[30] Fix | Delete
$this->wpdb_obj = $wpdb;
[31] Fix | Delete
[32] Fix | Delete
$mysql_dbh = false;
[33] Fix | Delete
$use_mysqli = false;
[34] Fix | Delete
[35] Fix | Delete
if (!$this->use_wpdb) {
[36] Fix | Delete
// We have our own extension which drops lots of the overhead on the query
[37] Fix | Delete
$wpdb_obj = $updraftplus_restorer->get_db_object();
[38] Fix | Delete
// Was that successful?
[39] Fix | Delete
if (!$wpdb_obj->is_mysql || !$wpdb_obj->ready) {
[40] Fix | Delete
$this->use_wpdb = true;
[41] Fix | Delete
} else {
[42] Fix | Delete
$this->wpdb_obj = $wpdb_obj;
[43] Fix | Delete
$mysql_dbh = $wpdb_obj->updraftplus_get_database_handle();
[44] Fix | Delete
$use_mysqli = $wpdb_obj->updraftplus_use_mysqli();
[45] Fix | Delete
}
[46] Fix | Delete
}
[47] Fix | Delete
[48] Fix | Delete
$this->mysql_dbh = $mysql_dbh;
[49] Fix | Delete
$this->use_mysqli = $use_mysqli;
[50] Fix | Delete
}
[51] Fix | Delete
[52] Fix | Delete
/**
[53] Fix | Delete
* The engine
[54] Fix | Delete
*
[55] Fix | Delete
* @param string|array $search - a string or array of things to search for
[56] Fix | Delete
* @param string|array $replace - a string or array of things to replace the search terms with
[57] Fix | Delete
* @param array $tables - an array of tables
[58] Fix | Delete
* @param integer $page_size - the page size
[59] Fix | Delete
*/
[60] Fix | Delete
public function icit_srdb_replacer($search, $replace, $tables, $page_size) {
[61] Fix | Delete
[62] Fix | Delete
if (!is_array($tables)) return false;
[63] Fix | Delete
[64] Fix | Delete
global $wpdb, $updraftplus;
[65] Fix | Delete
[66] Fix | Delete
$report = array(
[67] Fix | Delete
'tables' => 0,
[68] Fix | Delete
'rows' => 0,
[69] Fix | Delete
'change' => 0,
[70] Fix | Delete
'updates' => 0,
[71] Fix | Delete
'start' => microtime(true),
[72] Fix | Delete
'end' => microtime(true),
[73] Fix | Delete
'errors' => array(),
[74] Fix | Delete
);
[75] Fix | Delete
[76] Fix | Delete
$page_size = (empty($page_size) || !is_numeric($page_size)) ? 5000 : $page_size;
[77] Fix | Delete
[78] Fix | Delete
foreach ($tables as $table => $stripped_table) {
[79] Fix | Delete
[80] Fix | Delete
$report['tables']++;
[81] Fix | Delete
[82] Fix | Delete
if ($search === $replace) {
[83] Fix | Delete
$updraftplus->log("No search/replace required: would-be search and replacement are identical");
[84] Fix | Delete
continue;
[85] Fix | Delete
}
[86] Fix | Delete
[87] Fix | Delete
$this->columns = array();
[88] Fix | Delete
[89] Fix | Delete
$print_line = __('Search and replacing table:', 'updraftplus').' '.$table;
[90] Fix | Delete
[91] Fix | Delete
$updraftplus->check_db_connection($this->wpdb_obj, true);
[92] Fix | Delete
[93] Fix | Delete
// Get a list of columns in this table
[94] Fix | Delete
$fields = $wpdb->get_results('DESCRIBE '.UpdraftPlus_Manipulation_Functions::backquote($table), ARRAY_A);
[95] Fix | Delete
[96] Fix | Delete
$prikey_field = false;
[97] Fix | Delete
foreach ($fields as $column) {
[98] Fix | Delete
$primary_key = ('PRI' == $column['Key']) ? true : false;
[99] Fix | Delete
if ($primary_key) $prikey_field = $column['Field'];
[100] Fix | Delete
if ('posts' == $stripped_table && 'guid' == $column['Field']) {
[101] Fix | Delete
$updraftplus->log('Skipping search/replace on GUID column in posts table');
[102] Fix | Delete
continue;
[103] Fix | Delete
}
[104] Fix | Delete
$this->columns[$column['Field']] = $primary_key;
[105] Fix | Delete
}
[106] Fix | Delete
[107] Fix | Delete
// Count the number of rows we have in the table if large we'll split into blocks, This is a mod from Simon Wheatley
[108] Fix | Delete
[109] Fix | Delete
// InnoDB does not do count(*) quickly. You can use an index for more speed - see: http://www.cloudspace.com/blog/2009/08/06/fast-mysql-innodb-count-really-fast/
[110] Fix | Delete
[111] Fix | Delete
$where = '';
[112] Fix | Delete
// Opportunity to use internal knowledge on tables which may be huge
[113] Fix | Delete
if ('postmeta' == $stripped_table && ((is_array($search) && 0 === strpos($search[0], 'http')) || (is_string($search) && 0 === strpos($search, 'http')))) {
[114] Fix | Delete
$where = " WHERE meta_value LIKE '%http%'";
[115] Fix | Delete
}
[116] Fix | Delete
[117] Fix | Delete
$count_rows_sql = 'SELECT COUNT(*) FROM '.$table;
[118] Fix | Delete
if ($prikey_field) $count_rows_sql .= " USE INDEX (PRIMARY)";
[119] Fix | Delete
$count_rows_sql .= $where;
[120] Fix | Delete
[121] Fix | Delete
$row_countr = $wpdb->get_results($count_rows_sql, ARRAY_N);
[122] Fix | Delete
[123] Fix | Delete
// If that failed, try this
[124] Fix | Delete
if (false !== $prikey_field && $wpdb->last_error) {
[125] Fix | Delete
$row_countr = $wpdb->get_results("SELECT COUNT(*) FROM $table USE INDEX ($prikey_field)".$where, ARRAY_N);
[126] Fix | Delete
if ($wpdb->last_error) $row_countr = $wpdb->get_results("SELECT COUNT(*) FROM $table", ARRAY_N);
[127] Fix | Delete
}
[128] Fix | Delete
[129] Fix | Delete
$row_count = $row_countr[0][0];
[130] Fix | Delete
$print_line .= ': '.sprintf(__('rows: %d', 'updraftplus'), $row_count);
[131] Fix | Delete
$updraftplus->log($print_line, 'notice-restore', 'restoring-table-'.$table);
[132] Fix | Delete
$updraftplus->log('Search and replacing table: '.$table.": rows: ".$row_count);
[133] Fix | Delete
[134] Fix | Delete
if (0 == $row_count) continue;
[135] Fix | Delete
[136] Fix | Delete
for ($on_row = 0; $on_row <= $row_count; $on_row = $on_row+$page_size) {
[137] Fix | Delete
[138] Fix | Delete
$this->current_row = 0;
[139] Fix | Delete
[140] Fix | Delete
if ($on_row>0) $updraftplus->log_e("Searching and replacing reached row: %d", $on_row);
[141] Fix | Delete
[142] Fix | Delete
// Grab the contents of the table
[143] Fix | Delete
list($data, $page_size) = $this->fetch_sql_result($table, $on_row, $page_size, $where);
[144] Fix | Delete
// $sql_line is calculated here only for the purpose of logging errors
[145] Fix | Delete
// $where might contain a %, so don't place it inside the main parameter
[146] Fix | Delete
[147] Fix | Delete
$sql_line = sprintf('SELECT * FROM %s LIMIT %d, %d', $table.$where, $on_row, $on_row+$page_size);
[148] Fix | Delete
[149] Fix | Delete
// Our strategy here is to minimise memory usage if possible; to process one row at a time if we can, rather than reading everything into memory
[150] Fix | Delete
if ($this->use_wpdb) {
[151] Fix | Delete
[152] Fix | Delete
if ($wpdb->last_error) {
[153] Fix | Delete
$report['errors'][] = $this->print_error($sql_line);
[154] Fix | Delete
} else {
[155] Fix | Delete
foreach ($data as $row) {
[156] Fix | Delete
$rowrep = $this->process_row($table, $row, $search, $replace, $stripped_table);
[157] Fix | Delete
$report['rows']++;
[158] Fix | Delete
$report['updates'] += $rowrep['updates'];
[159] Fix | Delete
$report['change'] += $rowrep['change'];
[160] Fix | Delete
foreach ($rowrep['errors'] as $err) $report['errors'][] = $err;
[161] Fix | Delete
}
[162] Fix | Delete
}
[163] Fix | Delete
} else {
[164] Fix | Delete
if (false === $data) {
[165] Fix | Delete
$report['errors'][] = $this->print_error($sql_line);
[166] Fix | Delete
} elseif (true !== $data && null !== $data) {
[167] Fix | Delete
if ($this->use_mysqli) {
[168] Fix | Delete
while ($row = mysqli_fetch_array($data)) {
[169] Fix | Delete
$rowrep = $this->process_row($table, $row, $search, $replace, $stripped_table);
[170] Fix | Delete
$report['rows']++;
[171] Fix | Delete
$report['updates'] += $rowrep['updates'];
[172] Fix | Delete
$report['change'] += $rowrep['change'];
[173] Fix | Delete
foreach ($rowrep['errors'] as $err) $report['errors'][] = $err;
[174] Fix | Delete
}
[175] Fix | Delete
mysqli_free_result($data);
[176] Fix | Delete
} else {
[177] Fix | Delete
// phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
[178] Fix | Delete
while ($row = mysql_fetch_array($data)) {
[179] Fix | Delete
$rowrep = $this->process_row($table, $row, $search, $replace, $stripped_table);
[180] Fix | Delete
$report['rows']++;
[181] Fix | Delete
$report['updates'] += $rowrep['updates'];
[182] Fix | Delete
$report['change'] += $rowrep['change'];
[183] Fix | Delete
foreach ($rowrep['errors'] as $err) $report['errors'][] = $err;
[184] Fix | Delete
}
[185] Fix | Delete
// phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
[186] Fix | Delete
@mysql_free_result($data);
[187] Fix | Delete
}
[188] Fix | Delete
}
[189] Fix | Delete
}
[190] Fix | Delete
[191] Fix | Delete
}
[192] Fix | Delete
[193] Fix | Delete
}
[194] Fix | Delete
[195] Fix | Delete
$report['end'] = microtime(true);
[196] Fix | Delete
[197] Fix | Delete
return $report;
[198] Fix | Delete
}
[199] Fix | Delete
[200] Fix | Delete
/**
[201] Fix | Delete
* This function will get data from the passed in table ready to be search and replaced
[202] Fix | Delete
*
[203] Fix | Delete
* @param string $table - the table name
[204] Fix | Delete
* @param integer $on_row - the row to start from
[205] Fix | Delete
* @param integer $page_size - the page size
[206] Fix | Delete
* @param string $where - the where condition
[207] Fix | Delete
*
[208] Fix | Delete
* @return array - an array of data or an array with a false value
[209] Fix | Delete
*/
[210] Fix | Delete
private function fetch_sql_result($table, $on_row, $page_size, $where = '') {
[211] Fix | Delete
[212] Fix | Delete
$sql_line = sprintf('SELECT * FROM %s%s LIMIT %d, %d', $table, $where, $on_row, $page_size);
[213] Fix | Delete
[214] Fix | Delete
global $updraftplus;
[215] Fix | Delete
$updraftplus->check_db_connection($this->wpdb_obj, true);
[216] Fix | Delete
[217] Fix | Delete
if ($this->use_wpdb) {
[218] Fix | Delete
global $wpdb;
[219] Fix | Delete
$data = $wpdb->get_results($sql_line, ARRAY_A);
[220] Fix | Delete
if (!$wpdb->last_error) return array($data, $page_size);
[221] Fix | Delete
} else {
[222] Fix | Delete
if ($this->use_mysqli) {
[223] Fix | Delete
$data = mysqli_query($this->mysql_dbh, $sql_line);
[224] Fix | Delete
} else {
[225] Fix | Delete
// phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
[226] Fix | Delete
$data = mysql_query($sql_line, $this->mysql_dbh);
[227] Fix | Delete
}
[228] Fix | Delete
if (false !== $data) return array($data, $page_size);
[229] Fix | Delete
}
[230] Fix | Delete
[231] Fix | Delete
if (5000 <= $page_size) return $this->fetch_sql_result($table, $on_row, 2000, $where);
[232] Fix | Delete
if (2000 <= $page_size) return $this->fetch_sql_result($table, $on_row, 500, $where);
[233] Fix | Delete
[234] Fix | Delete
// At this point, $page_size should be 500; and that failed
[235] Fix | Delete
return array(false, $page_size);
[236] Fix | Delete
[237] Fix | Delete
}
[238] Fix | Delete
[239] Fix | Delete
/**
[240] Fix | Delete
* This function will process a single row from the database calling recursive_unserialize_replace to search and replace the data found in the search and replace arrays
[241] Fix | Delete
*
[242] Fix | Delete
* @param string $table - the current table we are working on
[243] Fix | Delete
* @param array $row - the current row we are working on
[244] Fix | Delete
* @param array $search - an array of things to search for
[245] Fix | Delete
* @param array $replace - an array of things to replace the search terms with
[246] Fix | Delete
* @param string $stripped_table - the stripped table
[247] Fix | Delete
*
[248] Fix | Delete
* @return array - returns an array report which includes changes made and any errors
[249] Fix | Delete
*/
[250] Fix | Delete
private function process_row($table, $row, $search, $replace, $stripped_table) {
[251] Fix | Delete
[252] Fix | Delete
global $updraftplus, $wpdb, $updraftplus_restorer;
[253] Fix | Delete
[254] Fix | Delete
$report = array('change' => 0, 'errors' => array(), 'updates' => 0);
[255] Fix | Delete
[256] Fix | Delete
$this->current_row++;
[257] Fix | Delete
[258] Fix | Delete
$update_sql = array();
[259] Fix | Delete
$where_sql = array();
[260] Fix | Delete
$upd = false;
[261] Fix | Delete
[262] Fix | Delete
foreach ($this->columns as $column => $primary_key) {
[263] Fix | Delete
[264] Fix | Delete
// Don't search/replace these
[265] Fix | Delete
if (('options' == $stripped_table && 'option_value' == $column && !empty($row['option_name']) && 'updraft_remotesites' == $row['option_name']) || ('sitemeta' == $stripped_table && 'meta_value' == $column && !empty($row['meta_key']) && 'updraftplus_options' == $row['meta_key'])) {
[266] Fix | Delete
continue;
[267] Fix | Delete
}
[268] Fix | Delete
[269] Fix | Delete
$edited_data = $data_to_fix = $row[$column];
[270] Fix | Delete
$successful = false;
[271] Fix | Delete
[272] Fix | Delete
// We catch errors/exceptions so that they're not fatal. Once saw a fatal ("Cannot access empty property") on "if (is_a($value, '__PHP_Incomplete_Class')) {" (not clear what $value has to be to cause that).
[273] Fix | Delete
try {
[274] Fix | Delete
// Run a search replace on the data that'll respect the serialisation.
[275] Fix | Delete
$edited_data = $this->recursive_unserialize_replace($search, $replace, $data_to_fix);
[276] Fix | Delete
$successful = true;
[277] Fix | Delete
} catch (Exception $e) {
[278] Fix | Delete
$log_message = 'An Exception ('.get_class($e).') occurred during the recursive search/replace. Exception message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
[279] Fix | Delete
$report['errors'][] = $log_message;
[280] Fix | Delete
error_log($log_message);
[281] Fix | Delete
$updraftplus->log($log_message);
[282] Fix | Delete
$updraftplus->log(sprintf(__('A PHP exception (%s) has occurred: %s', 'updraftplus'), get_class($e), $e->getMessage()), 'warning-restore');
[283] Fix | Delete
// @codingStandardsIgnoreLine
[284] Fix | Delete
} catch (Error $e) {
[285] Fix | Delete
$log_message = 'A PHP Fatal error (recoverable, '.get_class($e).') occurred during the recursive search/replace. Exception message: Error message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
[286] Fix | Delete
$report['errors'][] = $log_message;
[287] Fix | Delete
error_log($log_message);
[288] Fix | Delete
$updraftplus->log($log_message);
[289] Fix | Delete
$updraftplus->log(sprintf(__('A PHP fatal error (%s) has occurred: %s', 'updraftplus'), get_class($e), $e->getMessage()), 'warning-restore');
[290] Fix | Delete
}
[291] Fix | Delete
[292] Fix | Delete
// Something was changed
[293] Fix | Delete
if ($successful && $edited_data != $data_to_fix) {
[294] Fix | Delete
$report['change']++;
[295] Fix | Delete
$ed = $edited_data;
[296] Fix | Delete
$wpdb->escape_by_ref($ed);
[297] Fix | Delete
// Undo breakage introduced in WP 4.8.3 core
[298] Fix | Delete
if (is_callable(array($wpdb, 'remove_placeholder_escape'))) $ed = $wpdb->remove_placeholder_escape($ed);
[299] Fix | Delete
$update_sql[] = UpdraftPlus_Manipulation_Functions::backquote($column) . ' = "' . $ed . '"';
[300] Fix | Delete
$upd = true;
[301] Fix | Delete
}
[302] Fix | Delete
[303] Fix | Delete
if ($primary_key) {
[304] Fix | Delete
$df = $data_to_fix;
[305] Fix | Delete
$wpdb->escape_by_ref($df);
[306] Fix | Delete
// Undo breakage introduced in WP 4.8.3 core
[307] Fix | Delete
if (is_callable(array($wpdb, 'remove_placeholder_escape'))) $df = $wpdb->remove_placeholder_escape($df);
[308] Fix | Delete
$where_sql[] = UpdraftPlus_Manipulation_Functions::backquote($column) . ' = "' . $df . '"';
[309] Fix | Delete
}
[310] Fix | Delete
}
[311] Fix | Delete
[312] Fix | Delete
if ($upd && !empty($where_sql)) {
[313] Fix | Delete
$sql = 'UPDATE '.UpdraftPlus_Manipulation_Functions::backquote($table).' SET '.implode(', ', $update_sql).' WHERE '.implode(' AND ', array_filter($where_sql));
[314] Fix | Delete
$result = $updraftplus_restorer->sql_exec($sql, 5, '', false);
[315] Fix | Delete
if (false === $result || is_wp_error($result)) {
[316] Fix | Delete
$last_error = $this->print_error($sql);
[317] Fix | Delete
$report['errors'][] = $last_error;
[318] Fix | Delete
} else {
[319] Fix | Delete
$report['updates']++;
[320] Fix | Delete
}
[321] Fix | Delete
[322] Fix | Delete
} elseif ($upd) {
[323] Fix | Delete
$report['errors'][] = sprintf('"%s" has no primary key, manual change needed on row %s.', $table, $this->current_row);
[324] Fix | Delete
$updraftplus->log(__('Error:', 'updraftplus').' '.sprintf(__('"%s" has no primary key, manual change needed on row %s.', 'updraftplus'), $table, $this->current_row), 'warning-restore');
[325] Fix | Delete
}
[326] Fix | Delete
[327] Fix | Delete
return $report;
[328] Fix | Delete
[329] Fix | Delete
}
[330] Fix | Delete
[331] Fix | Delete
/**
[332] Fix | Delete
* Inspect incomplete class object and make a note in the restoration log if it is a new class
[333] Fix | Delete
*
[334] Fix | Delete
* @param object $data Object expected to be of __PHP_Incomplete_Class_Name
[335] Fix | Delete
*/
[336] Fix | Delete
private function unserialize_log_incomplete_class($data) {
[337] Fix | Delete
global $updraftplus;
[338] Fix | Delete
[339] Fix | Delete
try {
[340] Fix | Delete
$patch_object = new ArrayObject($data);
[341] Fix | Delete
$class_name = $patch_object['__PHP_Incomplete_Class_Name'];
[342] Fix | Delete
} catch (Exception $e) {
[343] Fix | Delete
error_log('unserialize_log_incomplete_class: '.$e->getMessage());
[344] Fix | Delete
// @codingStandardsIgnoreLine
[345] Fix | Delete
} catch (Error $e) {
[346] Fix | Delete
error_log('unserialize_log_incomplete_class: '.$e->getMessage());
[347] Fix | Delete
}
[348] Fix | Delete
[349] Fix | Delete
// Check if this class is known
[350] Fix | Delete
// Have to serialize incomplete class to find original class name
[351] Fix | Delete
if (!in_array($class_name, $this->known_incomplete_classes)) {
[352] Fix | Delete
$this->known_incomplete_classes[] = $class_name;
[353] Fix | Delete
$updraftplus->log('Incomplete object detected in database: '.$class_name.'; Search and replace will be skipped for these entries');
[354] Fix | Delete
}
[355] Fix | Delete
}
[356] Fix | Delete
[357] Fix | Delete
/**
[358] Fix | Delete
* Take a serialised array and unserialise it replacing elements as needed and
[359] Fix | Delete
* unserialising any subordinate arrays and performing the replace on those too.
[360] Fix | Delete
* N.B. $from and $to can be arrays - they get passed only to str_replace(), which can take an array
[361] Fix | Delete
*
[362] Fix | Delete
* @param string $from String we're looking to replace.
[363] Fix | Delete
* @param string $to What we want it to be replaced with
[364] Fix | Delete
* @param array $data Used to pass any subordinate arrays back to in.
[365] Fix | Delete
* @param bool $serialised Does the array passed via $data need serialising.
[366] Fix | Delete
*
[367] Fix | Delete
* @return array The original array with all elements replaced as needed.
[368] Fix | Delete
*/
[369] Fix | Delete
private function recursive_unserialize_replace($from = '', $to = '', $data = '', $serialised = false) {
[370] Fix | Delete
[371] Fix | Delete
global $updraftplus;
[372] Fix | Delete
[373] Fix | Delete
static $error_count = 0;
[374] Fix | Delete
[375] Fix | Delete
// some unserialised data cannot be re-serialised eg. SimpleXMLElements
[376] Fix | Delete
try {
[377] Fix | Delete
$case_insensitive = false;
[378] Fix | Delete
[379] Fix | Delete
if (is_array($from) && is_array($to)) {
[380] Fix | Delete
$case_insensitive = preg_match('#^https?:#i', implode($from)) && preg_match('#^https?:#i', implode($to)) ? true : false;
[381] Fix | Delete
} else {
[382] Fix | Delete
$case_insensitive = preg_match('#^https?:#i', $from) && preg_match('#^https?:#i', $to) ? true : false;
[383] Fix | Delete
}
[384] Fix | Delete
[385] Fix | Delete
// O:8:"DateTime":0:{} : see https://bugs.php.net/bug.php?id=62852
[386] Fix | Delete
if (is_serialized($data) && false === strpos($data, 'O:8:"DateTime":0:{}') && false !== ($unserialized = @unserialize($data))) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
[387] Fix | Delete
$data = $this->recursive_unserialize_replace($from, $to, $unserialized, true);
[388] Fix | Delete
} elseif (is_array($data)) {
[389] Fix | Delete
$_tmp = array();
[390] Fix | Delete
foreach ($data as $key => $value) {
[391] Fix | Delete
// Check that we aren't attempting search/replace on an incomplete class
[392] Fix | Delete
// We assume that if $data is an __PHP_Incomplete_Class, it is extremely likely that the original did not contain the domain
[393] Fix | Delete
if (is_a($value, '__PHP_Incomplete_Class')) {
[394] Fix | Delete
// Check if this class is known
[395] Fix | Delete
$this->unserialize_log_incomplete_class($value);
[396] Fix | Delete
[397] Fix | Delete
// return original data
[398] Fix | Delete
$_tmp[$key] = $value;
[399] Fix | Delete
} else {
[400] Fix | Delete
$_tmp[$key] = $this->recursive_unserialize_replace($from, $to, $value, false);
[401] Fix | Delete
}
[402] Fix | Delete
}
[403] Fix | Delete
[404] Fix | Delete
$data = $_tmp;
[405] Fix | Delete
unset($_tmp);
[406] Fix | Delete
} elseif (is_object($data)) {
[407] Fix | Delete
$_tmp = $data; // new $data_class();
[408] Fix | Delete
// Check that we aren't attempting search/replace on an incomplete class
[409] Fix | Delete
// We assume that if $data is an __PHP_Incomplete_Class, it is extremely likely that the original did not contain the domain
[410] Fix | Delete
if (is_a($data, '__PHP_Incomplete_Class')) {
[411] Fix | Delete
// Check if this class is known
[412] Fix | Delete
$this->unserialize_log_incomplete_class($data);
[413] Fix | Delete
} else {
[414] Fix | Delete
$props = get_object_vars($data);
[415] Fix | Delete
foreach ($props as $key => $value) {
[416] Fix | Delete
$_tmp->$key = $this->recursive_unserialize_replace($from, $to, $value, false);
[417] Fix | Delete
}
[418] Fix | Delete
}
[419] Fix | Delete
$data = $_tmp;
[420] Fix | Delete
unset($_tmp);
[421] Fix | Delete
} elseif (is_string($data) && (null !== ($_tmp = json_decode($data, true)))) {
[422] Fix | Delete
[423] Fix | Delete
if (is_array($_tmp)) {
[424] Fix | Delete
foreach ($_tmp as $key => $value) {
[425] Fix | Delete
// Check that we aren't attempting search/replace on an incomplete class
[426] Fix | Delete
// We assume that if $data is an __PHP_Incomplete_Class, it is extremely likely that the original did not contain the domain
[427] Fix | Delete
if (is_a($value, '__PHP_Incomplete_Class')) {
[428] Fix | Delete
// Check if this class is known
[429] Fix | Delete
$this->unserialize_log_incomplete_class($value);
[430] Fix | Delete
[431] Fix | Delete
// return original data
[432] Fix | Delete
$_tmp[$key] = $value;
[433] Fix | Delete
} else {
[434] Fix | Delete
$_tmp[$key] = $this->recursive_unserialize_replace($from, $to, $value, false);
[435] Fix | Delete
}
[436] Fix | Delete
}
[437] Fix | Delete
[438] Fix | Delete
$data = json_encode($_tmp);
[439] Fix | Delete
unset($_tmp);
[440] Fix | Delete
}
[441] Fix | Delete
[442] Fix | Delete
} else {
[443] Fix | Delete
if (is_string($data)) {
[444] Fix | Delete
if ($case_insensitive) {
[445] Fix | Delete
$data = str_ireplace($from, $to, $data);
[446] Fix | Delete
} else {
[447] Fix | Delete
$data = str_replace($from, $to, $data);
[448] Fix | Delete
}
[449] Fix | Delete
// Below is the wrong approach. In fact, in the problematic case, the resolution is an extra search/replace to undo unnecessary ones
[450] Fix | Delete
// if (is_string($from)) {
[451] Fix | Delete
// $data = str_replace($from, $to, $data);
[452] Fix | Delete
// } else {
[453] Fix | Delete
// # Array. We only want a maximum of one replacement to take place. This is only an issue in non-default setups, but in those situations, carrying out all the search/replaces can be wrong. This is also why the most specific URL should be done first.
[454] Fix | Delete
// foreach ($from as $i => $f) {
[455] Fix | Delete
// $ndata = str_replace($f, $to[$i], $data);
[456] Fix | Delete
// if ($ndata != $data) {
[457] Fix | Delete
// $data = $ndata;
[458] Fix | Delete
// break;
[459] Fix | Delete
// }
[460] Fix | Delete
// }
[461] Fix | Delete
// }
[462] Fix | Delete
}
[463] Fix | Delete
}
[464] Fix | Delete
[465] Fix | Delete
if ($serialised)
[466] Fix | Delete
return serialize($data);
[467] Fix | Delete
[468] Fix | Delete
} catch (Exception $error) {
[469] Fix | Delete
if (3 > $error_count) {
[470] Fix | Delete
$log_message = 'PHP Fatal Exception error ('.get_class($error).') has occurred during recursive_unserialize_replace. Error Message: '.$error->getMessage().' (Code: '.$error->getCode().', line '.$error->getLine().' in '.$error->getFile().')';
[471] Fix | Delete
$updraftplus->log($log_message, 'warning-restore');
[472] Fix | Delete
$error_count++;
[473] Fix | Delete
}
[474] Fix | Delete
}
[475] Fix | Delete
[476] Fix | Delete
return $data;
[477] Fix | Delete
}
[478] Fix | Delete
[479] Fix | Delete
/**
[480] Fix | Delete
* This function will get the last database error and log it
[481] Fix | Delete
*
[482] Fix | Delete
* @param string $sql_line - the sql line that caused the error
[483] Fix | Delete
*
[484] Fix | Delete
* @return void
[485] Fix | Delete
*/
[486] Fix | Delete
public function print_error($sql_line) {
[487] Fix | Delete
global $wpdb, $updraftplus;
[488] Fix | Delete
if ($this->use_wpdb) {
[489] Fix | Delete
$last_error = $wpdb->last_error;
[490] Fix | Delete
} else {
[491] Fix | Delete
// phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
[492] Fix | Delete
$last_error = ($this->use_mysqli) ? mysqli_error($this->mysql_dbh) : mysql_error($this->mysql_dbh);
[493] Fix | Delete
}
[494] Fix | Delete
$updraftplus->log(__('Error:', 'updraftplus')." ".$last_error." - ".__('the database query being run was:', 'updraftplus').' '.$sql_line, 'warning-restore');
[495] Fix | Delete
return $last_error;
[496] Fix | Delete
}
[497] Fix | Delete
}
[498] Fix | Delete
[499] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function