[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/phpbb/db/ -> migrator.php (source)

   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\db;
  15  
  16  use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  17  use Symfony\Component\DependencyInjection\ContainerInterface;
  18  
  19  /**
  20  * The migrator is responsible for applying new migrations in the correct order.
  21  */
  22  class migrator
  23  {
  24      /**
  25       * @var ContainerInterface
  26       */
  27      protected $container;
  28  
  29      /** @var \phpbb\config\config */
  30      protected $config;
  31  
  32      /** @var \phpbb\db\driver\driver_interface */
  33      protected $db;
  34  
  35      /** @var \phpbb\db\tools */
  36      protected $db_tools;
  37  
  38      /** @var \phpbb\db\migration\helper */
  39      protected $helper;
  40  
  41      /** @var string */
  42      protected $table_prefix;
  43  
  44      /** @var string */
  45      protected $phpbb_root_path;
  46  
  47      /** @var string */
  48      protected $php_ext;
  49  
  50      /** @var string */
  51      protected $migrations_table;
  52  
  53      /**
  54      * State of all migrations
  55      *
  56      * (SELECT * FROM migrations table)
  57      *
  58      * @var array
  59      */
  60      protected $migration_state = array();
  61  
  62      /**
  63      * Array of all migrations available to be run
  64      *
  65      * @var array
  66      */
  67      protected $migrations = array();
  68  
  69      /**
  70      * Array of migrations that have been determined to be fulfillable
  71      *
  72      * @var array
  73      */
  74      protected $fulfillable_migrations = array();
  75  
  76      /**
  77      * 'name,' 'class,' and 'state' of the last migration run
  78      *
  79      * 'effectively_installed' set and set to true if the migration was effectively_installed
  80      *
  81      * @var array
  82      */
  83      protected $last_run_migration = false;
  84  
  85      /**
  86       * The output handler. A null handler is configured by default.
  87       *
  88       * @var migrator_output_handler_interface
  89       */
  90      protected $output_handler;
  91  
  92      /**
  93      * Constructor of the database migrator
  94      */
  95  	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)
  96      {
  97          $this->container = $container;
  98          $this->config = $config;
  99          $this->db = $db;
 100          $this->db_tools = $db_tools;
 101          $this->helper = $helper;
 102  
 103          $this->migrations_table = $migrations_table;
 104  
 105          $this->phpbb_root_path = $phpbb_root_path;
 106          $this->php_ext = $php_ext;
 107  
 108          $this->table_prefix = $table_prefix;
 109  
 110          $this->output_handler = new null_migrator_output_handler();
 111  
 112          foreach ($tools as $tool)
 113          {
 114              $this->tools[$tool->get_name()] = $tool;
 115          }
 116  
 117          $this->tools['dbtools'] = $this->db_tools;
 118  
 119          $this->load_migration_state();
 120      }
 121  
 122      /**
 123       * Set the output handler.
 124       *
 125       * @param migrator_output_handler $handler The output handler
 126       */
 127  	public function set_output_handler(migrator_output_handler_interface $handler)
 128      {
 129          $this->output_handler = $handler;
 130      }
 131  
 132      /**
 133      * Loads all migrations and their application state from the database.
 134      *
 135      * @return null
 136      */
 137  	public function load_migration_state()
 138      {
 139          $this->migration_state = array();
 140  
 141          // prevent errors in case the table does not exist yet
 142          $this->db->sql_return_on_error(true);
 143  
 144          $sql = "SELECT *
 145              FROM " . $this->migrations_table;
 146          $result = $this->db->sql_query($sql);
 147  
 148          if (!$this->db->get_sql_error_triggered())
 149          {
 150              while ($migration = $this->db->sql_fetchrow($result))
 151              {
 152                  $this->migration_state[$migration['migration_name']] = $migration;
 153  
 154                  $this->migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']);
 155                  $this->migration_state[$migration['migration_name']]['migration_data_state'] = !empty($migration['migration_data_state']) ? unserialize($migration['migration_data_state']) : '';
 156              }
 157          }
 158  
 159          $this->db->sql_freeresult($result);
 160  
 161          $this->db->sql_return_on_error(false);
 162      }
 163  
 164      /**
 165       * Get an array with information about the last migration run.
 166       *
 167       * The array contains 'name', 'class' and 'state'. 'effectively_installed' is set
 168       * and set to true if the last migration was effectively_installed.
 169       *
 170       * @return array
 171       */
 172  	public function get_last_run_migration()
 173      {
 174          return $this->last_run_migration;
 175      }
 176  
 177      /**
 178      * Sets the list of available migration class names to the given array.
 179      *
 180      * @param array $class_names An array of migration class names
 181      * @return null
 182      */
 183  	public function set_migrations($class_names)
 184      {
 185          $this->migrations = $class_names;
 186      }
 187  
 188      /**
 189      * Runs a single update step from the next migration to be applied.
 190      *
 191      * The update step can either be a schema or a (partial) data update. To
 192      * check if update() needs to be called again use the finished() method.
 193      *
 194      * @return null
 195      */
 196  	public function update()
 197      {
 198          $this->container->get('dispatcher')->disable();
 199          $this->update_do();
 200          $this->container->get('dispatcher')->enable();
 201      }
 202  
 203      /**
 204       * Get a valid migration name from the migration state array in case the
 205       * supplied name is not in the migration state list.
 206       *
 207       * @param string $name Migration name
 208       * @return string Migration name
 209       */
 210  	protected function get_valid_name($name)
 211      {
 212          // Try falling back to a valid migration name with or without leading backslash
 213          if (!isset($this->migration_state[$name]))
 214          {
 215              $prepended_name = ($name[0] == '\\' ? '' : '\\') . $name;
 216              $prefixless_name = $name[0] == '\\' ? substr($name, 1) : $name;
 217  
 218              if (isset($this->migration_state[$prepended_name]))
 219              {
 220                  $name = $prepended_name;
 221              }
 222              else if (isset($this->migration_state[$prefixless_name]))
 223              {
 224                  $name = $prefixless_name;
 225              }
 226          }
 227  
 228          return $name;
 229      }
 230  
 231      /**
 232       * Effectively runs a single update step from the next migration to be applied.
 233       *
 234       * @return null
 235       */
 236  	protected function update_do()
 237      {
 238          foreach ($this->migrations as $name)
 239          {
 240              $name = $this->get_valid_name($name);
 241  
 242              if (!isset($this->migration_state[$name]) ||
 243                  !$this->migration_state[$name]['migration_schema_done'] ||
 244                  !$this->migration_state[$name]['migration_data_done'])
 245              {
 246                  if (!$this->try_apply($name))
 247                  {
 248                      continue;
 249                  }
 250                  else
 251                  {
 252                      return;
 253                  }
 254              }
 255              else
 256              {
 257                  $this->output_handler->write(array('MIGRATION_EFFECTIVELY_INSTALLED', $name), migrator_output_handler_interface::VERBOSITY_DEBUG);
 258              }
 259          }
 260      }
 261  
 262      /**
 263      * Attempts to apply a step of the given migration or one of its dependencies
 264      *
 265      * @param    string    $name The class name of the migration
 266      * @return    bool    Whether any update step was successfully run
 267      * @throws \phpbb\db\migration\exception
 268      */
 269  	protected function try_apply($name)
 270      {
 271          if (!class_exists($name))
 272          {
 273              $this->output_handler->write(array('MIGRATION_NOT_VALID', $name), migrator_output_handler_interface::VERBOSITY_DEBUG);
 274              return false;
 275          }
 276  
 277          $migration = $this->get_migration($name);
 278  
 279          $state = (isset($this->migration_state[$name])) ?
 280              $this->migration_state[$name] :
 281              array(
 282                  'migration_depends_on'    => $migration->depends_on(),
 283                  'migration_schema_done' => false,
 284                  'migration_data_done'    => false,
 285                  'migration_data_state'    => '',
 286                  'migration_start_time'    => 0,
 287                  'migration_end_time'    => 0,
 288              );
 289  
 290          if (!empty($state['migration_depends_on']))
 291          {
 292              $this->output_handler->write(array('MIGRATION_APPLY_DEPENDENCIES', $name), migrator_output_handler_interface::VERBOSITY_DEBUG);
 293          }
 294  
 295          foreach ($state['migration_depends_on'] as $depend)
 296          {
 297              $depend = $this->get_valid_name($depend);
 298  
 299              // Test all possible namings before throwing exception
 300              if ($this->unfulfillable($depend) !== false)
 301              {
 302                  throw new \phpbb\db\migration\exception('MIGRATION_NOT_FULFILLABLE', $name, $depend);
 303              }
 304  
 305              if (!isset($this->migration_state[$depend]) ||
 306                  !$this->migration_state[$depend]['migration_schema_done'] ||
 307                  !$this->migration_state[$depend]['migration_data_done'])
 308              {
 309                  return $this->try_apply($depend);
 310              }
 311          }
 312  
 313          $this->last_run_migration = array(
 314              'name'    => $name,
 315              'class'    => $migration,
 316              'state'    => $state,
 317              'task'    => '',
 318          );
 319  
 320          if (!isset($this->migration_state[$name]))
 321          {
 322              if ($state['migration_start_time'] == 0 && $migration->effectively_installed())
 323              {
 324                  $state = array(
 325                      'migration_depends_on'    => $migration->depends_on(),
 326                      'migration_schema_done' => true,
 327                      'migration_data_done'    => true,
 328                      'migration_data_state'    => '',
 329                      'migration_start_time'    => 0,
 330                      'migration_end_time'    => 0,
 331                  );
 332  
 333                  $this->last_run_migration['effectively_installed'] = true;
 334  
 335                  $this->output_handler->write(array('MIGRATION_EFFECTIVELY_INSTALLED', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
 336              }
 337              else
 338              {
 339                  $state['migration_start_time'] = time();
 340              }
 341          }
 342  
 343          $this->set_migration_state($name, $state);
 344  
 345          if (!$state['migration_schema_done'])
 346          {
 347              $verbosity = empty($state['migration_data_state']) ?
 348                  migrator_output_handler_interface::VERBOSITY_VERBOSE : migrator_output_handler_interface::VERBOSITY_DEBUG;
 349              $this->output_handler->write(array('MIGRATION_SCHEMA_RUNNING', $name), $verbosity);
 350  
 351              $this->last_run_migration['task'] = 'process_schema_step';
 352  
 353              $total_time = (is_array($state['migration_data_state']) && isset($state['migration_data_state']['_total_time'])) ?
 354                  $state['migration_data_state']['_total_time'] : 0.0;
 355              $elapsed_time = microtime(true);
 356  
 357              $steps = $this->helper->get_schema_steps($migration->update_schema());
 358              $result = $this->process_data_step($steps, $state['migration_data_state']);
 359  
 360              $elapsed_time = microtime(true) - $elapsed_time;
 361              $total_time += $elapsed_time;
 362  
 363              if (is_array($result))
 364              {
 365                  $result['_total_time'] = $total_time;
 366              }
 367  
 368              $state['migration_data_state'] = ($result === true) ? '' : $result;
 369              $state['migration_schema_done'] = ($result === true);
 370  
 371              if ($state['migration_schema_done'])
 372              {
 373                  $this->output_handler->write(array('MIGRATION_SCHEMA_DONE', $name, $total_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
 374              }
 375              else
 376              {
 377                  $this->output_handler->write(array('MIGRATION_SCHEMA_IN_PROGRESS', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_VERY_VERBOSE);
 378              }
 379          }
 380          else if (!$state['migration_data_done'])
 381          {
 382              try
 383              {
 384                  $verbosity = empty($state['migration_data_state']) ?
 385                      migrator_output_handler_interface::VERBOSITY_VERBOSE : migrator_output_handler_interface::VERBOSITY_DEBUG;
 386                  $this->output_handler->write(array('MIGRATION_DATA_RUNNING', $name), $verbosity);
 387  
 388                  $this->last_run_migration['task'] = 'process_data_step';
 389  
 390                  $total_time = (is_array($state['migration_data_state']) && isset($state['migration_data_state']['_total_time'])) ?
 391                      $state['migration_data_state']['_total_time'] : 0.0;
 392                  $elapsed_time = microtime(true);
 393  
 394                  $result = $this->process_data_step($migration->update_data(), $state['migration_data_state']);
 395  
 396                  $elapsed_time = microtime(true) - $elapsed_time;
 397                  $total_time += $elapsed_time;
 398  
 399                  if (is_array($result))
 400                  {
 401                      $result['_total_time'] = $total_time;
 402                  }
 403  
 404                  $state['migration_data_state'] = ($result === true) ? '' : $result;
 405                  $state['migration_data_done'] = ($result === true);
 406                  $state['migration_end_time'] = ($result === true) ? time() : 0;
 407  
 408                  if ($state['migration_data_done'])
 409                  {
 410                      $this->output_handler->write(array('MIGRATION_DATA_DONE', $name, $total_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
 411                  }
 412                  else
 413                  {
 414                      $this->output_handler->write(array('MIGRATION_DATA_IN_PROGRESS', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_VERY_VERBOSE);
 415                  }
 416              }
 417              catch (\phpbb\db\migration\exception $e)
 418              {
 419                  // Reset data state and revert the schema changes
 420                  $state['migration_data_state'] = '';
 421                  $this->set_migration_state($name, $state);
 422  
 423                  $this->revert_do($name);
 424  
 425                  throw $e;
 426              }
 427          }
 428  
 429          $this->set_migration_state($name, $state);
 430  
 431          return true;
 432      }
 433  
 434      /**
 435      * Runs a single revert step from the last migration installed
 436      *
 437      * YOU MUST ADD/SET ALL MIGRATIONS THAT COULD BE DEPENDENT ON THE MIGRATION TO REVERT TO BEFORE CALLING THIS METHOD!
 438      * The revert step can either be a schema or a (partial) data revert. To
 439      * check if revert() needs to be called again use the migration_state() method.
 440      *
 441      * @param string $migration String migration name to revert (including any that depend on this migration)
 442      */
 443  	public function revert($migration)
 444      {
 445          $this->container->get('dispatcher')->disable();
 446          $this->revert_do($migration);
 447          $this->container->get('dispatcher')->enable();
 448      }
 449  
 450      /**
 451       * Effectively runs a single revert step from the last migration installed
 452       *
 453       * @param string $migration String migration name to revert (including any that depend on this migration)
 454       * @return null
 455       */
 456  	protected function revert_do($migration)
 457      {
 458          if (!isset($this->migration_state[$migration]))
 459          {
 460              // Not installed
 461              return;
 462          }
 463  
 464          foreach ($this->migration_state as $name => $state)
 465          {
 466              if (!empty($state['migration_depends_on']) && in_array($migration, $state['migration_depends_on']))
 467              {
 468                  $this->revert_do($name);
 469              }
 470          }
 471  
 472          $this->try_revert($migration);
 473      }
 474  
 475      /**
 476      * Attempts to revert a step of the given migration or one of its dependencies
 477      *
 478      * @param    string    $name The class name of the migration
 479      * @return    bool    Whether any update step was successfully run
 480      */
 481  	protected function try_revert($name)
 482      {
 483          if (!class_exists($name))
 484          {
 485              return false;
 486          }
 487  
 488          $migration = $this->get_migration($name);
 489  
 490          $state = $this->migration_state[$name];
 491  
 492          $this->last_run_migration = array(
 493              'name'    => $name,
 494              'class'    => $migration,
 495              'task'    => '',
 496          );
 497  
 498          if ($state['migration_data_done'])
 499          {
 500              $steps = array_merge($this->helper->reverse_update_data($migration->update_data()), $migration->revert_data());
 501              $result = $this->process_data_step($steps, $state['migration_data_state']);
 502  
 503              $state['migration_data_state'] = ($result === true) ? '' : $result;
 504              $state['migration_data_done'] = ($result === true) ? false : true;
 505  
 506              $this->set_migration_state($name, $state);
 507          }
 508          else if ($state['migration_schema_done'])
 509          {
 510              $steps = $this->helper->get_schema_steps($migration->revert_schema());
 511              $result = $this->process_data_step($steps, $state['migration_data_state']);
 512  
 513              $state['migration_data_state'] = ($result === true) ? '' : $result;
 514              $state['migration_schema_done'] = ($result === true) ? false : true;
 515  
 516              if (!$state['migration_schema_done'])
 517              {
 518                  $sql = 'DELETE FROM ' . $this->migrations_table . "
 519                      WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
 520                  $this->db->sql_query($sql);
 521  
 522                  $this->last_run_migration = false;
 523                  unset($this->migration_state[$name]);
 524              }
 525              else
 526              {
 527                  $this->set_migration_state($name, $state);
 528              }
 529          }
 530  
 531          return true;
 532      }
 533  
 534      /**
 535      * Process the data step of the migration
 536      *
 537      * @param array $steps The steps to run
 538      * @param bool|string $state Current state of the migration
 539      * @param bool $revert true to revert a data step
 540      * @return bool|string migration state. True if completed, serialized array if not finished
 541      * @throws \phpbb\db\migration\exception
 542      */
 543  	protected function process_data_step($steps, $state, $revert = false)
 544      {
 545          if (sizeof($steps) === 0)
 546          {
 547              return true;
 548          }
 549  
 550          $state = is_array($state) ? $state : false;
 551  
 552          // reverse order of steps if reverting
 553          if ($revert === true)
 554          {
 555              $steps = array_reverse($steps);
 556          }
 557  
 558          $step = $last_result = 0;
 559          if ($state)
 560          {
 561              $step = $state['step'];
 562  
 563              // We send the result from last time to the callable function
 564              $last_result = $state['result'];
 565          }
 566  
 567          try
 568          {
 569              // Result will be null or true if everything completed correctly
 570              // Stop after each update step, to let the updater control the script runtime
 571              $result = $this->run_step($steps[$step], $last_result, $revert);
 572              if (($result !== null && $result !== true) || $step + 1 < sizeof($steps))
 573              {
 574                  return array(
 575                      'result'    => $result,
 576                      // Move on if the last call finished
 577                      'step'        => ($result !== null && $result !== true) ? $step : $step + 1,
 578                  );
 579              }
 580          }
 581          catch (\phpbb\db\migration\exception $e)
 582          {
 583              // We should try rolling back here
 584              foreach ($steps as $reverse_step_identifier => $reverse_step)
 585              {
 586                  // If we've reached the current step we can break because we reversed everything that was run
 587                  if ($reverse_step_identifier == $step)
 588                  {
 589                      break;
 590                  }
 591  
 592                  // Reverse the step that was run
 593                  $result = $this->run_step($reverse_step, false, !$revert);
 594              }
 595  
 596              throw $e;
 597          }
 598  
 599          return true;
 600      }
 601  
 602      /**
 603      * Run a single step
 604      *
 605      * An exception should be thrown if an error occurs
 606      *
 607      * @param mixed $step Data step from migration
 608      * @param mixed $last_result Result to pass to the callable (only for 'custom' method)
 609      * @param bool $reverse False to install, True to attempt uninstallation by reversing the call
 610      * @return null
 611      */
 612  	protected function run_step($step, $last_result = 0, $reverse = false)
 613      {
 614          $callable_and_parameters = $this->get_callable_from_step($step, $last_result, $reverse);
 615  
 616          if ($callable_and_parameters === false)
 617          {
 618              return;
 619          }
 620  
 621          $callable = $callable_and_parameters[0];
 622          $parameters = $callable_and_parameters[1];
 623  
 624          return call_user_func_array($callable, $parameters);
 625      }
 626  
 627      /**
 628      * Get a callable statement from a data step
 629      *
 630      * @param array $step Data step from migration
 631      * @param mixed $last_result Result to pass to the callable (only for 'custom' method)
 632      * @param bool $reverse False to install, True to attempt uninstallation by reversing the call
 633      * @return array Array with parameters for call_user_func_array(), 0 is the callable, 1 is parameters
 634      * @throws \phpbb\db\migration\exception
 635      */
 636  	protected function get_callable_from_step(array $step, $last_result = 0, $reverse = false)
 637      {
 638          $type = $step[0];
 639          $parameters = $step[1];
 640  
 641          $parts = explode('.', $type);
 642  
 643          $class = $parts[0];
 644          $method = false;
 645  
 646          if (isset($parts[1]))
 647          {
 648              $method = $parts[1];
 649          }
 650  
 651          switch ($class)
 652          {
 653              case 'if':
 654                  if (!isset($parameters[0]))
 655                  {
 656                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_CONDITION', $step);
 657                  }
 658  
 659                  if (!isset($parameters[1]))
 660                  {
 661                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_STEP', $step);
 662                  }
 663  
 664                  if ($reverse)
 665                  {
 666                      // We might get unexpected results when trying
 667                      // to revert this, so just avoid it
 668                      return false;
 669                  }
 670  
 671                  $condition = $parameters[0];
 672  
 673                  if (!$condition)
 674                  {
 675                      return false;
 676                  }
 677  
 678                  $step = $parameters[1];
 679  
 680                  return $this->get_callable_from_step($step);
 681              break;
 682  
 683              case 'custom':
 684                  if (!is_callable($parameters[0]))
 685                  {
 686                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_CUSTOM_NOT_CALLABLE', $step);
 687                  }
 688  
 689                  if ($reverse)
 690                  {
 691                      return false;
 692                  }
 693                  else
 694                  {
 695                      return array(
 696                          $parameters[0],
 697                          array($last_result),
 698                      );
 699                  }
 700              break;
 701  
 702              default:
 703                  if (!$method)
 704                  {
 705                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNKNOWN_TYPE', $step);
 706                  }
 707  
 708                  if (!isset($this->tools[$class]))
 709                  {
 710                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNDEFINED_TOOL', $step);
 711                  }
 712  
 713                  if (!method_exists(get_class($this->tools[$class]), $method))
 714                  {
 715                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNDEFINED_METHOD', $step);
 716                  }
 717  
 718                  // Attempt to reverse operations
 719                  if ($reverse)
 720                  {
 721                      array_unshift($parameters, $method);
 722  
 723                      return array(
 724                          array($this->tools[$class], 'reverse'),
 725                          $parameters,
 726                      );
 727                  }
 728  
 729                  return array(
 730                      array($this->tools[$class], $method),
 731                      $parameters,
 732                  );
 733              break;
 734          }
 735      }
 736  
 737      /**
 738      * Insert/Update migration row into the database
 739      *
 740      * @param string $name Name of the migration
 741      * @param array $state
 742      * @return null
 743      */
 744  	protected function set_migration_state($name, $state)
 745      {
 746          $migration_row = $state;
 747          $migration_row['migration_depends_on'] = serialize($state['migration_depends_on']);
 748          $migration_row['migration_data_state'] = !empty($state['migration_data_state']) ? serialize($state['migration_data_state']) : '';
 749  
 750          if (isset($this->migration_state[$name]))
 751          {
 752              $sql = 'UPDATE ' . $this->migrations_table . '
 753                  SET ' . $this->db->sql_build_array('UPDATE', $migration_row) . "
 754                  WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
 755              $this->db->sql_query($sql);
 756          }
 757          else
 758          {
 759              $migration_row['migration_name'] = $name;
 760              $sql = 'INSERT INTO ' . $this->migrations_table . '
 761                  ' . $this->db->sql_build_array('INSERT', $migration_row);
 762              $this->db->sql_query($sql);
 763          }
 764  
 765          $this->migration_state[$name] = $state;
 766  
 767          $this->last_run_migration['state'] = $state;
 768      }
 769  
 770      /**
 771      * Checks if a migration's dependencies can even theoretically be satisfied.
 772      *
 773      * @param string    $name The class name of the migration
 774      * @return bool|string False if fulfillable, string of missing migration name if unfulfillable
 775      */
 776  	public function unfulfillable($name)
 777      {
 778          $name = $this->get_valid_name($name);
 779  
 780          if (isset($this->migration_state[$name]) || isset($this->fulfillable_migrations[$name]))
 781          {
 782              return false;
 783          }
 784  
 785          if (!class_exists($name))
 786          {
 787              return $name;
 788          }
 789  
 790          $migration = $this->get_migration($name);
 791          $depends = $migration->depends_on();
 792  
 793          foreach ($depends as $depend)
 794          {
 795              $depend = $this->get_valid_name($depend);
 796              $unfulfillable = $this->unfulfillable($depend);
 797              if ($unfulfillable !== false)
 798              {
 799                  return $unfulfillable;
 800              }
 801          }
 802          $this->fulfillable_migrations[$name] = true;
 803  
 804          return false;
 805      }
 806  
 807      /**
 808      * Checks whether all available, fulfillable migrations have been applied.
 809      *
 810      * @return bool Whether the migrations have been applied
 811      */
 812  	public function finished()
 813      {
 814          foreach ($this->migrations as $name)
 815          {
 816              if (!isset($this->migration_state[$name]))
 817              {
 818                  // skip unfulfillable migrations, but fulfillables mean we
 819                  // are not finished yet
 820                  if ($this->unfulfillable($name) !== false)
 821                  {
 822                      continue;
 823                  }
 824  
 825                  return false;
 826              }
 827  
 828              $migration = $this->migration_state[$name];
 829  
 830              if (!$migration['migration_schema_done'] || !$migration['migration_data_done'])
 831              {
 832                  return false;
 833              }
 834          }
 835  
 836          return true;
 837      }
 838  
 839      /**
 840      * Gets a migration state (whether it is installed and to what extent)
 841      *
 842      * @param string $migration String migration name to check if it is installed
 843      * @return bool|array False if the migration has not at all been installed, array
 844      */
 845  	public function migration_state($migration)
 846      {
 847          if (!isset($this->migration_state[$migration]))
 848          {
 849              return false;
 850          }
 851  
 852          return $this->migration_state[$migration];
 853      }
 854  
 855      /**
 856      * Helper to get a migration
 857      *
 858      * @param string $name Name of the migration
 859      * @return \phpbb\db\migration\migration
 860      */
 861  	protected function get_migration($name)
 862      {
 863          $migration = new $name($this->config, $this->db, $this->db_tools, $this->phpbb_root_path, $this->php_ext, $this->table_prefix);
 864  
 865          if ($migration instanceof ContainerAwareInterface)
 866          {
 867              $migration->setContainer($this->container);
 868          }
 869  
 870          return $migration;
 871      }
 872  
 873      /**
 874      * This function adds all migrations sent to it to the migrations table
 875      *
 876      * THIS SHOULD NOT GENERALLY BE USED! THIS IS FOR THE PHPBB INSTALLER.
 877      * THIS WILL THROW ERRORS IF MIGRATIONS ALREADY EXIST IN THE TABLE, DO NOT CALL MORE THAN ONCE!
 878      *
 879      * @param array $migrations Array of migrations (names) to add to the migrations table
 880      * @return null
 881      */
 882  	public function populate_migrations($migrations)
 883      {
 884          foreach ($migrations as $name)
 885          {
 886              if ($this->migration_state($name) === false)
 887              {
 888                  $state = array(
 889                      'migration_depends_on'    => $name::depends_on(),
 890                      'migration_schema_done' => true,
 891                      'migration_data_done'    => true,
 892                      'migration_data_state'    => '',
 893                      'migration_start_time'    => time(),
 894                      'migration_end_time'    => time(),
 895                  );
 896                  $this->set_migration_state($name, $state);
 897              }
 898          }
 899      }
 900  
 901      /**
 902      * Creates the migrations table if it does not exist.
 903      * @return null
 904      */
 905  	public function create_migrations_table()
 906      {
 907          // Make sure migrations have been installed.
 908          if (!$this->db_tools->sql_table_exists($this->table_prefix . 'migrations'))
 909          {
 910              $this->db_tools->sql_create_table($this->table_prefix . 'migrations', array(
 911                  'COLUMNS'        => array(
 912                      'migration_name'            => array('VCHAR', ''),
 913                      'migration_depends_on'        => array('TEXT', ''),
 914                      'migration_schema_done'        => array('BOOL', 0),
 915                      'migration_data_done'        => array('BOOL', 0),
 916                      'migration_data_state'        => array('TEXT', ''),
 917                      'migration_start_time'        => array('TIMESTAMP', 0),
 918                      'migration_end_time'        => array('TIMESTAMP', 0),
 919                  ),
 920                  'PRIMARY_KEY'    => 'migration_name',
 921              ));
 922          }
 923      }
 924  }


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1