phpBB

Code Changes

File: phpbb/db/migrator.php

  Unmodified   Added   Modified   Removed
Line 12Line 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 57Line 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 65Line 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 136Line 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 164Line 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 222Line 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 269Line 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 309Line 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 330Line 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 344Line 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 376Line 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 406Line 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 424Line 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 432Line 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 468Line 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 477Line 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 545Line 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 624Line 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 653Line 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 668Line 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 734Line 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;

	}

/**

	}

/**