Line 12 | Line 12 |
---|
*/
namespace phpbb\db;
|
*/
namespace phpbb\db;
|
| use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
|
/** * The migrator is responsible for applying new migrations in the correct order. */ class migrator {
|
/** * The migrator is responsible for applying new migrations in the correct order. */ class migrator {
|
| /** * @var ContainerInterface */ protected $container;
|
/** @var \phpbb\config\config */ protected $config;
| /** @var \phpbb\config\config */ protected $config;
|
Line 57 | Line 65 |
---|
* @var array */ protected $migrations = array();
|
* @var array */ protected $migrations = array();
|
| /** * Array of migrations that have been determined to be fulfillable * * @var array */ protected $fulfillable_migrations = array();
|
/** * 'name,' 'class,' and 'state' of the last migration run
| /** * 'name,' 'class,' and 'state' of the last migration run
|
Line 65 | Line 80 |
---|
* * @var array */
|
* * @var array */
|
public $last_run_migration = false;
| protected $last_run_migration = false;
|
/** * The output handler. A null handler is configured by default. *
|
/** * The output handler. A null handler is configured by default. *
|
* @var migrator_output_handler
| * @var migrator_output_handler_interface
|
*/
|
*/
|
public $output_handler;
| protected $output_handler;
|
/** * Constructor of the database migrator */
|
/** * Constructor of the database migrator */
|
public function __construct(\phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools $db_tools, $migrations_table, $phpbb_root_path, $php_ext, $table_prefix, $tools, \phpbb\db\migration\helper $helper)
| public function __construct(ContainerInterface $container, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools $db_tools, $migrations_table, $phpbb_root_path, $php_ext, $table_prefix, $tools, \phpbb\db\migration\helper $helper)
|
{
|
{
|
| $this->container = $container;
|
$this->config = $config; $this->db = $db; $this->db_tools = $db_tools;
| $this->config = $config; $this->db = $db; $this->db_tools = $db_tools;
|
Line 136 | Line 152 |
---|
$this->migration_state[$migration['migration_name']] = $migration;
$this->migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']);
|
$this->migration_state[$migration['migration_name']] = $migration;
$this->migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']);
|
| $this->migration_state[$migration['migration_name']]['migration_data_state'] = !empty($migration['migration_data_state']) ? unserialize($migration['migration_data_state']) : '';
|
} }
$this->db->sql_freeresult($result);
$this->db->sql_return_on_error(false);
|
} }
$this->db->sql_freeresult($result);
$this->db->sql_return_on_error(false);
|
| }
/** * Get an array with information about the last migration run. * * The array contains 'name', 'class' and 'state'. 'effectively_installed' is set * and set to true if the last migration was effectively_installed. * * @return array */ public function get_last_run_migration() { return $this->last_run_migration;
|
}
/**
| }
/**
|
Line 164 | Line 194 |
---|
* @return null */ public function update()
|
* @return null */ public function update()
|
| { $this->container->get('dispatcher')->disable(); $this->update_do(); $this->container->get('dispatcher')->enable(); }
/** * Get a valid migration name from the migration state array in case the * supplied name is not in the migration state list. * * @param string $name Migration name * @return string Migration name */ protected function get_valid_name($name) { // Try falling back to a valid migration name with or without leading backslash if (!isset($this->migration_state[$name])) { $prepended_name = ($name[0] == '\\' ? '' : '\\') . $name; $prefixless_name = $name[0] == '\\' ? substr($name, 1) : $name;
if (isset($this->migration_state[$prepended_name])) { $name = $prepended_name; } else if (isset($this->migration_state[$prefixless_name])) { $name = $prefixless_name; } }
return $name; }
/** * Effectively runs a single update step from the next migration to be applied. * * @return null */ protected function update_do()
|
{ foreach ($this->migrations as $name) {
|
{ foreach ($this->migrations as $name) {
|
| $name = $this->get_valid_name($name);
|
if (!isset($this->migration_state[$name]) || !$this->migration_state[$name]['migration_schema_done'] || !$this->migration_state[$name]['migration_data_done'])
| if (!isset($this->migration_state[$name]) || !$this->migration_state[$name]['migration_schema_done'] || !$this->migration_state[$name]['migration_data_done'])
|
Line 222 | Line 294 |
---|
foreach ($state['migration_depends_on'] as $depend) {
|
foreach ($state['migration_depends_on'] as $depend) {
|
| $depend = $this->get_valid_name($depend);
// Test all possible namings before throwing exception
|
if ($this->unfulfillable($depend) !== false) { throw new \phpbb\db\migration\exception('MIGRATION_NOT_FULFILLABLE', $name, $depend);
| if ($this->unfulfillable($depend) !== false) { throw new \phpbb\db\migration\exception('MIGRATION_NOT_FULFILLABLE', $name, $depend);
|
Line 269 | Line 344 |
---|
if (!$state['migration_schema_done']) {
|
if (!$state['migration_schema_done']) {
|
$this->output_handler->write(array('MIGRATION_SCHEMA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
| $verbosity = empty($state['migration_data_state']) ? migrator_output_handler_interface::VERBOSITY_VERBOSE : migrator_output_handler_interface::VERBOSITY_DEBUG; $this->output_handler->write(array('MIGRATION_SCHEMA_RUNNING', $name), $verbosity);
|
$this->last_run_migration['task'] = 'process_schema_step';
|
$this->last_run_migration['task'] = 'process_schema_step';
|
| $total_time = (is_array($state['migration_data_state']) && isset($state['migration_data_state']['_total_time'])) ? $state['migration_data_state']['_total_time'] : 0.0;
|
$elapsed_time = microtime(true);
|
$elapsed_time = microtime(true);
|
|
|
$steps = $this->helper->get_schema_steps($migration->update_schema()); $result = $this->process_data_step($steps, $state['migration_data_state']);
|
$steps = $this->helper->get_schema_steps($migration->update_schema()); $result = $this->process_data_step($steps, $state['migration_data_state']);
|
|
|
$elapsed_time = microtime(true) - $elapsed_time;
|
$elapsed_time = microtime(true) - $elapsed_time;
|
| $total_time += $elapsed_time;
if (is_array($result)) { $result['_total_time'] = $total_time; }
|
$state['migration_data_state'] = ($result === true) ? '' : $result; $state['migration_schema_done'] = ($result === true);
|
$state['migration_data_state'] = ($result === true) ? '' : $result; $state['migration_schema_done'] = ($result === true);
|
$this->output_handler->write(array('MIGRATION_SCHEMA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
| if ($state['migration_schema_done']) { $this->output_handler->write(array('MIGRATION_SCHEMA_DONE', $name, $total_time), migrator_output_handler_interface::VERBOSITY_NORMAL); } else { $this->output_handler->write(array('MIGRATION_SCHEMA_IN_PROGRESS', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_VERY_VERBOSE); }
|
} else if (!$state['migration_data_done']) { try {
|
} else if (!$state['migration_data_done']) { try {
|
$this->output_handler->write(array('MIGRATION_DATA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
| $verbosity = empty($state['migration_data_state']) ? migrator_output_handler_interface::VERBOSITY_VERBOSE : migrator_output_handler_interface::VERBOSITY_DEBUG; $this->output_handler->write(array('MIGRATION_DATA_RUNNING', $name), $verbosity);
|
$this->last_run_migration['task'] = 'process_data_step';
|
$this->last_run_migration['task'] = 'process_data_step';
|
| $total_time = (is_array($state['migration_data_state']) && isset($state['migration_data_state']['_total_time'])) ? $state['migration_data_state']['_total_time'] : 0.0;
|
$elapsed_time = microtime(true);
|
$elapsed_time = microtime(true);
|
|
|
$result = $this->process_data_step($migration->update_data(), $state['migration_data_state']);
|
$result = $this->process_data_step($migration->update_data(), $state['migration_data_state']);
|
|
|
$elapsed_time = microtime(true) - $elapsed_time;
|
$elapsed_time = microtime(true) - $elapsed_time;
|
| $total_time += $elapsed_time;
if (is_array($result)) { $result['_total_time'] = $total_time; }
|
$state['migration_data_state'] = ($result === true) ? '' : $result; $state['migration_data_done'] = ($result === true); $state['migration_end_time'] = ($result === true) ? time() : 0;
|
$state['migration_data_state'] = ($result === true) ? '' : $result; $state['migration_data_done'] = ($result === true); $state['migration_end_time'] = ($result === true) ? time() : 0;
|
if ($state['migration_schema_done'])
| if ($state['migration_data_done'])
|
{
|
{
|
$this->output_handler->write(array('MIGRATION_DATA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
| $this->output_handler->write(array('MIGRATION_DATA_DONE', $name, $total_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
|
} else {
| } else {
|
Line 309 | Line 416 |
---|
} catch (\phpbb\db\migration\exception $e) {
|
} catch (\phpbb\db\migration\exception $e) {
|
// Revert the schema changes $this->revert($name);
| // Reset data state and revert the schema changes $state['migration_data_state'] = ''; $this->set_migration_state($name, $state);
$this->revert_do($name);
|
|
|
// Rethrow exception
| |
throw $e; } }
| throw $e; } }
|
Line 330 | Line 439 |
---|
* check if revert() needs to be called again use the migration_state() method. * * @param string $migration String migration name to revert (including any that depend on this migration)
|
* check if revert() needs to be called again use the migration_state() method. * * @param string $migration String migration name to revert (including any that depend on this migration)
|
* @return null
| |
*/ public function revert($migration)
|
*/ public function revert($migration)
|
| { $this->container->get('dispatcher')->disable(); $this->revert_do($migration); $this->container->get('dispatcher')->enable(); }
/** * Effectively runs a single revert step from the last migration installed * * @param string $migration String migration name to revert (including any that depend on this migration) * @return null */ protected function revert_do($migration)
|
{ if (!isset($this->migration_state[$migration])) {
| { if (!isset($this->migration_state[$migration])) {
|
Line 344 | Line 465 |
---|
{ if (!empty($state['migration_depends_on']) && in_array($migration, $state['migration_depends_on'])) {
|
{ if (!empty($state['migration_depends_on']) && in_array($migration, $state['migration_depends_on'])) {
|
$this->revert($name);
| $this->revert_do($name);
|
} }
| } }
|
Line 376 | Line 497 |
---|
if ($state['migration_data_done']) {
|
if ($state['migration_data_done']) {
|
if ($state['migration_data_state'] !== 'revert_data') { $result = $this->process_data_step($migration->update_data(), $state['migration_data_state'], true);
$state['migration_data_state'] = ($result === true) ? 'revert_data' : $result; } else { $result = $this->process_data_step($migration->revert_data(), '', false);
| $steps = array_merge($this->helper->reverse_update_data($migration->update_data()), $migration->revert_data()); $result = $this->process_data_step($steps, $state['migration_data_state']);
|
$state['migration_data_state'] = ($result === true) ? '' : $result; $state['migration_data_done'] = ($result === true) ? false : true;
|
$state['migration_data_state'] = ($result === true) ? '' : $result; $state['migration_data_done'] = ($result === true) ? false : true;
|
}
| |
$this->set_migration_state($name, $state); }
| $this->set_migration_state($name, $state); }
|
Line 406 | Line 519 |
---|
WHERE migration_name = '" . $this->db->sql_escape($name) . "'"; $this->db->sql_query($sql);
|
WHERE migration_name = '" . $this->db->sql_escape($name) . "'"; $this->db->sql_query($sql);
|
| $this->last_run_migration = false;
|
unset($this->migration_state[$name]);
|
unset($this->migration_state[$name]);
|
| } else { $this->set_migration_state($name, $state);
|
} }
| } }
|
Line 424 | Line 542 |
---|
*/ protected function process_data_step($steps, $state, $revert = false) {
|
*/ protected function process_data_step($steps, $state, $revert = false) {
|
$state = ($state) ? unserialize($state) : false;
| if (sizeof($steps) === 0) { return true; }
$state = is_array($state) ? $state : false;
|
// reverse order of steps if reverting if ($revert === true)
| // reverse order of steps if reverting if ($revert === true)
|
Line 432 | Line 555 |
---|
$steps = array_reverse($steps); }
|
$steps = array_reverse($steps); }
|
foreach ($steps as $step_identifier => $step) { $last_result = 0;
| $step = $last_result = 0;
|
if ($state) {
|
if ($state) {
|
// Continue until we reach the step that matches the last step called if ($state['step'] != $step_identifier) { continue; }
| $step = $state['step'];
|
// We send the result from last time to the callable function $last_result = $state['result'];
|
// We send the result from last time to the callable function $last_result = $state['result'];
|
// Set state to false since we reached the point we were at $state = false;
| |
}
try { // Result will be null or true if everything completed correctly
|
}
try { // Result will be null or true if everything completed correctly
|
$result = $this->run_step($step, $last_result, $revert); if ($result !== null && $result !== true)
| // Stop after each update step, to let the updater control the script runtime $result = $this->run_step($steps[$step], $last_result, $revert); if (($result !== null && $result !== true) || $step + 1 < sizeof($steps))
|
{
|
{
|
return serialize(array(
| return array(
|
'result' => $result,
|
'result' => $result,
|
'step' => $step_identifier, ));
| // Move on if the last call finished 'step' => ($result !== null && $result !== true) ? $step : $step + 1, );
|
} } catch (\phpbb\db\migration\exception $e)
| } } catch (\phpbb\db\migration\exception $e)
|
Line 468 | Line 584 |
---|
foreach ($steps as $reverse_step_identifier => $reverse_step) { // If we've reached the current step we can break because we reversed everything that was run
|
foreach ($steps as $reverse_step_identifier => $reverse_step) { // If we've reached the current step we can break because we reversed everything that was run
|
if ($reverse_step_identifier == $step_identifier)
| if ($reverse_step_identifier == $step)
|
{ break; }
| { break; }
|
Line 477 | Line 593 |
---|
$result = $this->run_step($reverse_step, false, !$revert); }
|
$result = $this->run_step($reverse_step, false, !$revert); }
|
// rethrow the exception
| |
throw $e;
|
throw $e;
|
}
| |
}
return true;
| }
return true;
|
Line 545 | Line 659 |
---|
if (!isset($parameters[1])) { throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_STEP', $step);
|
if (!isset($parameters[1])) { throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_STEP', $step);
|
| }
if ($reverse) { // We might get unexpected results when trying // to revert this, so just avoid it return false;
|
}
$condition = $parameters[0];
| }
$condition = $parameters[0];
|
Line 624 | Line 745 |
---|
{ $migration_row = $state; $migration_row['migration_depends_on'] = serialize($state['migration_depends_on']);
|
{ $migration_row = $state; $migration_row['migration_depends_on'] = serialize($state['migration_depends_on']);
|
| $migration_row['migration_data_state'] = !empty($state['migration_data_state']) ? serialize($state['migration_data_state']) : '';
|
if (isset($this->migration_state[$name])) {
| if (isset($this->migration_state[$name])) {
|
Line 653 | Line 775 |
---|
*/ public function unfulfillable($name) {
|
*/ public function unfulfillable($name) {
|
if (isset($this->migration_state[$name]))
| $name = $this->get_valid_name($name);
if (isset($this->migration_state[$name]) || isset($this->fulfillable_migrations[$name]))
|
{ return false; }
| { return false; }
|
Line 668 | Line 792 |
---|
foreach ($depends as $depend) {
|
foreach ($depends as $depend) {
|
| $depend = $this->get_valid_name($depend);
|
$unfulfillable = $this->unfulfillable($depend); if ($unfulfillable !== false) { return $unfulfillable; } }
|
$unfulfillable = $this->unfulfillable($depend); if ($unfulfillable !== false) { return $unfulfillable; } }
|
| $this->fulfillable_migrations[$name] = true;
|
return false; }
| return false; }
|
Line 734 | Line 860 |
---|
*/ protected function get_migration($name) {
|
*/ protected function get_migration($name) {
|
return new $name($this->config, $this->db, $this->db_tools, $this->phpbb_root_path, $this->php_ext, $this->table_prefix);
| $migration = new $name($this->config, $this->db, $this->db_tools, $this->phpbb_root_path, $this->php_ext, $this->table_prefix);
if ($migration instanceof ContainerAwareInterface) { $migration->setContainer($this->container); }
return $migration;
|
}
/**
| }
/**
|