/** * {@inheritdoc} */ public function run() { // Include diff engine $this->update_helper->include_file('includes/diff/diff.' . $this->php_ext); $this->update_helper->include_file('includes/diff/engine.' . $this->php_ext); // Set up basic vars $old_path = $this->update_helper->get_path_to_old_update_files(); $new_path = $this->update_helper->get_path_to_new_update_files(); $files_to_diff = $this->installer_config->get('update_files', array()); $files_to_diff = $files_to_diff['update_with_diff']; // Set progress bar $this->iohandler->set_task_count(count($files_to_diff), true); $this->iohandler->set_progress('UPDATE_FILE_DIFF', 0); $progress_count = $this->installer_config->get('file_diff_update_count', 0); // Recover progress $progress_key = $this->installer_config->get('differ_progress_key', -1); $progress_recovered = $progress_key === -1; $merge_conflicts = $this->installer_config->get('merge_conflict_list', array()); foreach ($files_to_diff as $key => $filename) { if ($progress_recovered === false) { if ($progress_key === $key) { $progress_recovered = true; } continue; } // Read in files' content $file_contents = array(); // Handle the special case when user created a file with the filename that is now new in the core $file_contents[0] = file_exists($old_path . $filename) ? file_get_contents($old_path . $filename) : ''; $filenames = array($this->phpbb_root_path . $filename, $new_path . $filename); foreach ($filenames as $file_to_diff) { $file_contents[] = file_get_contents($file_to_diff); if ($file_contents[sizeof($file_contents) - 1] === false) { $this->iohandler->add_error_message(array('FILE_DIFFER_ERROR_FILE_CANNOT_BE_READ', $files_to_diff)); unset($file_contents); throw new user_interaction_required_exception(); } } $diff = new \diff3($file_contents[0], $file_contents[1], $file_contents[2]); unset($file_contents); // Handle conflicts if ($diff->get_num_conflicts() !== 0) { $merge_conflicts[] = $filename; } // Save merged output $this->cache->put('_file_' . md5($filename), base64_encode(implode("\n", $diff->merged_output()))); unset($diff); $progress_count++; $this->iohandler->set_progress('UPDATE_FILE_DIFF', $progress_count); if ($this->installer_config->get_time_remaining() <= 0 || $this->installer_config->get_memory_remaining() <= 0) { // Save differ progress $this->installer_config->set('differ_progress_key', $key); $this->installer_config->set('merge_conflict_list', $merge_conflicts); $this->installer_config->set('file_diff_update_count', $progress_count); // Request refresh throw new resource_limit_reached_exception(); } } $this->iohandler->finish_progress('ALL_FILES_DIFFED'); $this->installer_config->set('merge_conflict_list', $merge_conflicts); }
/** * Compare files for storage in update_list */ function make_update_diff(&$update_list, $original_file, $file, $custom = false) { global $phpbb_root_path, $user; $update_ary = array('filename' => $file, 'custom' => $custom); if ($custom) { $update_ary['original'] = $original_file; } // we only want to know if the files are successfully merged and newlines could result in errors (duplicate addition of lines and such things) // Therefore we check for empty diffs with two methods, preserving newlines and not preserving them (which mostly works best, therefore the first option) // On a successfull update the new location file exists but the old one does not exist. // Check for this circumstance, the new file need to be up-to-date with the current file then... if (!file_exists($this->old_location . $original_file) && file_exists($this->new_location . $original_file) && file_exists($phpbb_root_path . $file)) { $tmp = array( 'file1' => file_get_contents($this->new_location . $original_file), 'file2' => file_get_contents($phpbb_root_path . $file), ); // We need to diff the contents here to make sure the file is really the one we expect $diff = new diff($tmp['file1'], $tmp['file2'], false); $empty = $diff->is_empty(); unset($tmp, $diff); // if there are no differences we have an up-to-date file... if ($empty) { $update_list['up_to_date'][] = $update_ary; return; } // If no other status matches we have another file in the way... $update_list['new_conflict'][] = $update_ary; return; } // Old file removed? if (file_exists($this->old_location . $original_file) && !file_exists($this->new_location . $original_file)) { return; } // Check for existance, else abort immediately if (!file_exists($this->old_location . $original_file) || !file_exists($this->new_location . $original_file)) { trigger_error($user->lang['INCOMPLETE_UPDATE_FILES'], E_USER_ERROR); } $preserve_cr_ary = array(false, true); foreach ($preserve_cr_ary as $preserve_cr) { $tmp = array( 'file1' => file_get_contents($this->old_location . $original_file), 'file2' => file_get_contents($phpbb_root_path . $file), ); // We need to diff the contents here to make sure the file is really the one we expect $diff = new diff($tmp['file1'], $tmp['file2'], $preserve_cr); $empty_1 = $diff->is_empty(); unset($tmp, $diff); $tmp = array( 'file1' => file_get_contents($this->new_location . $original_file), 'file2' => file_get_contents($phpbb_root_path . $file), ); $diff = new diff($tmp['file1'], $tmp['file2'], $preserve_cr); $empty_2 = $diff->is_empty(); unset($tmp, $diff); // If the file is not modified we are finished here... if ($empty_1) { // Further check if it is already up to date - it could happen that non-modified files // slip through if ($empty_2) { $update_list['up_to_date'][] = $update_ary; return; } $update_list['not_modified'][] = $update_ary; return; } // If the file had been modified then we need to check if it is already up to date // if there are no differences we have an up-to-date file... if ($empty_2) { $update_list['up_to_date'][] = $update_ary; return; } } $conflicts = false; foreach ($preserve_cr_ary as $preserve_cr) { // if the file is modified we try to make sure a merge succeed $tmp = array( 'orig' => file_get_contents($this->old_location . $original_file), 'final1' => file_get_contents($phpbb_root_path . $file), 'final2' => file_get_contents($this->new_location . $original_file), ); $diff = new diff3($tmp['orig'], $tmp['final1'], $tmp['final2'], $preserve_cr); unset($tmp); if (!$diff->get_num_conflicts()) { $tmp = array( 'file1' => file_get_contents($phpbb_root_path . $file), 'file2' => implode("\n", $diff->merged_output()), ); // now compare the merged output with the original file to see if the modified file is up to date $diff2 = new diff($tmp['file1'], $tmp['file2'], $preserve_cr); $empty = $diff2->is_empty(); unset($diff, $diff2); if ($empty) { $update_list['up_to_date'][] = $update_ary; return; } // If we preserve cr tag it as modified because the conflict would not show in this mode anyway if ($preserve_cr) { $update_list['modified'][] = $update_ary; return; } } else { // There is one special case... users having merged with a conflicting file... we need to check this $tmp = array( 'file1' => file_get_contents($phpbb_root_path . $file), 'file2' => implode("\n", $diff->merged_new_output()), ); $diff2 = new diff($tmp['file1'], $tmp['file2'], $preserve_cr); $empty = $diff2->is_empty(); if (!$empty) { unset($tmp, $diff2); // We check if the user merged with his output $tmp = array( 'file1' => file_get_contents($phpbb_root_path . $file), 'file2' => implode("\n", $diff->merged_orig_output()), ); $diff2 = new diff($tmp['file1'], $tmp['file2'], $preserve_cr); $empty = $diff2->is_empty(); } if (!$empty) { $conflicts = $diff->get_num_conflicts(); } unset($diff, $diff2); if ($empty) { // A conflict got resolved... $update_list['up_to_date'][] = $update_ary; return; } } } if ($conflicts !== false) { $update_ary['conflicts'] = $conflicts; $update_list['conflict'][] = $update_ary; return; } // If no other status matches we have a modified file... $update_list['modified'][] = $update_ary; }