vendor/doctrine-migrations/lib/Doctrine/DBAL/Migrations/Version.php
changeset 39 03b14b0fe101
equal deleted inserted replaced
38:bbdc7f9aa25e 39:03b14b0fe101
       
     1 <?php
       
     2 /*
       
     3  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
     4  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
     5  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
     6  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
       
     7  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
     8  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
       
     9  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
       
    10  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
       
    11  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    12  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    13  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    14  *
       
    15  * This software consists of voluntary contributions made by many individuals
       
    16  * and is licensed under the LGPL. For more information, see
       
    17  * <http://www.doctrine-project.org>.
       
    18 */
       
    19 
       
    20 namespace Doctrine\DBAL\Migrations;
       
    21 
       
    22 use Doctrine\DBAL\Migrations\Configuration\Configuration,
       
    23     Doctrine\DBAL\Schema\Schema;
       
    24 
       
    25 /**
       
    26  * Class which wraps a migration version and allows execution of the
       
    27  * individual migration version up or down method.
       
    28  *
       
    29  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
       
    30  * @link        www.doctrine-project.org
       
    31  * @since       2.0
       
    32  * @author      Jonathan H. Wage <jonwage@gmail.com>
       
    33  */
       
    34 class Version
       
    35 {
       
    36     const STATE_NONE = 0;
       
    37     const STATE_PRE  = 1;
       
    38     const STATE_EXEC = 2;
       
    39     const STATE_POST = 3;
       
    40 
       
    41     /**
       
    42      * The Migrations Configuration instance for this migration
       
    43      *
       
    44      * @var Configuration
       
    45      */
       
    46     private $configuration;
       
    47 
       
    48     /**
       
    49      * The OutputWriter object instance used for outputting information
       
    50      *
       
    51      * @var OutputWriter
       
    52      */
       
    53     private $outputWriter;
       
    54 
       
    55     /**
       
    56      * The version in timestamp format (YYYYMMDDHHMMSS)
       
    57      *
       
    58      * @param int
       
    59      */
       
    60     private $version;
       
    61 
       
    62     /**
       
    63      * @var AbstractSchemaManager
       
    64      */
       
    65     private $sm;
       
    66 
       
    67     /**
       
    68      * @var AbstractPlatform
       
    69      */
       
    70     private $platform;
       
    71 
       
    72     /**
       
    73      * The migration instance for this version
       
    74      *
       
    75      * @var AbstractMigration
       
    76      */
       
    77     private $migration;
       
    78 
       
    79     /**
       
    80      * @var Connection
       
    81      */
       
    82     private $connection;
       
    83 
       
    84     /**
       
    85      * @var string
       
    86      */
       
    87     private $class;
       
    88 
       
    89     /** The array of collected SQL statements for this version */
       
    90     private $sql = array();
       
    91 
       
    92     /** The array of collected parameters for SQL statements for this version */
       
    93     private $params = array();
       
    94 
       
    95     /** The array of collected types for SQL statements for this version */
       
    96     private $types = array();
       
    97 
       
    98     /** The time in seconds that this migration version took to execute */
       
    99     private $time;
       
   100 
       
   101     /**
       
   102      * @var int
       
   103      */
       
   104     private $state = self::STATE_NONE;
       
   105 
       
   106     public function __construct(Configuration $configuration, $version, $class)
       
   107     {
       
   108         $this->configuration = $configuration;
       
   109         $this->outputWriter = $configuration->getOutputWriter();
       
   110         $this->class = $class;
       
   111         $this->connection = $configuration->getConnection();
       
   112         $this->sm = $this->connection->getSchemaManager();
       
   113         $this->platform = $this->connection->getDatabasePlatform();
       
   114         $this->migration = new $class($this);
       
   115         $this->version = $this->migration->getName() ?: $version;
       
   116     }
       
   117 
       
   118     /**
       
   119      * Returns the string version in the format YYYYMMDDHHMMSS
       
   120      *
       
   121      * @return string $version
       
   122      */
       
   123     public function getVersion()
       
   124     {
       
   125         return $this->version;
       
   126     }
       
   127 
       
   128     /**
       
   129      * Returns the Migrations Configuration object instance
       
   130      *
       
   131      * @return Configuration $configuration
       
   132      */
       
   133     public function getConfiguration()
       
   134     {
       
   135         return $this->configuration;
       
   136     }
       
   137 
       
   138     /**
       
   139      * Check if this version has been migrated or not.
       
   140      *
       
   141      * @param bool $bool
       
   142      * @return mixed
       
   143      */
       
   144     public function isMigrated()
       
   145     {
       
   146         return $this->configuration->hasVersionMigrated($this);
       
   147     }
       
   148 
       
   149     public function markMigrated()
       
   150     {
       
   151         $this->configuration->createMigrationTable();
       
   152         $this->connection->executeQuery("INSERT INTO " . $this->configuration->getMigrationsTableName() . " (version) VALUES (?)", array($this->version));
       
   153     }
       
   154 
       
   155     public function markNotMigrated()
       
   156     {
       
   157         $this->configuration->createMigrationTable();
       
   158         $this->connection->executeQuery("DELETE FROM " . $this->configuration->getMigrationsTableName() . " WHERE version = ?", array($this->version));
       
   159     }
       
   160 
       
   161     /**
       
   162      * Add some SQL queries to this versions migration
       
   163      *
       
   164      * @param mixed $sql
       
   165      * @param array $params
       
   166      * @param array $types
       
   167      * @return void
       
   168      */
       
   169     public function addSql($sql, array $params = array(), array $types = array())
       
   170     {
       
   171         if (is_array($sql)) {
       
   172             foreach ($sql as $key => $query) {
       
   173                 $this->sql[] = $query;
       
   174                 if (isset($params[$key])) {
       
   175                     $this->params[count($this->sql) - 1] = $params[$key];
       
   176                     $this->types[count($this->sql) - 1] = isset($types[$key]) ? $types[$key] : array();
       
   177                 }
       
   178             }
       
   179         } else {
       
   180             $this->sql[] = $sql;
       
   181             if ($params) {
       
   182                 $this->params[count($this->sql) - 1] = $params;
       
   183                 $this->types[count($this->sql) - 1] = $types ?: array();
       
   184             }
       
   185         }
       
   186     }
       
   187 
       
   188     /**
       
   189      * Write a migration SQL file to the given path
       
   190      *
       
   191      * @param string $path          The path to write the migration SQL file.
       
   192      * @param string $direction     The direction to execute.
       
   193      * @return bool $written
       
   194      */
       
   195     public function writeSqlFile($path, $direction = 'up')
       
   196     {
       
   197         $queries = $this->execute($direction, true);
       
   198 
       
   199         $string  = sprintf("# Doctrine Migration File Generated on %s\n", date('Y-m-d H:m:s'));
       
   200 
       
   201         $string .= "\n# Version " . $this->version . "\n";
       
   202         foreach ($queries as $query) {
       
   203             $string .= $query . ";\n";
       
   204         }
       
   205         if (is_dir($path)) {
       
   206             $path = realpath($path);
       
   207             $path = $path . '/doctrine_migration_' . date('YmdHis') . '.sql';
       
   208         }
       
   209 
       
   210         $this->outputWriter->write("\n".sprintf('Writing migration file to "<info>%s</info>"', $path));
       
   211 
       
   212         return file_put_contents($path, $string);
       
   213     }
       
   214 
       
   215     /**
       
   216      * @return AbstractMigration
       
   217      */
       
   218     public function getMigration()
       
   219     {
       
   220         return $this->migration;
       
   221     }
       
   222 
       
   223     /**
       
   224      * Execute this migration version up or down and and return the SQL.
       
   225      *
       
   226      * @param string $direction   The direction to execute the migration.
       
   227      * @param string $dryRun      Whether to not actually execute the migration SQL and just do a dry run.
       
   228      * @return array $sql
       
   229      * @throws Exception when migration fails
       
   230      */
       
   231     public function execute($direction, $dryRun = false)
       
   232     {
       
   233         $this->sql = array();
       
   234 
       
   235         $this->connection->beginTransaction();
       
   236 
       
   237         try {
       
   238             $start = microtime(true);
       
   239 
       
   240             $this->state = self::STATE_PRE;
       
   241             $fromSchema = $this->sm->createSchema();
       
   242             $this->migration->{'pre' . ucfirst($direction)}($fromSchema);
       
   243 
       
   244             if ($direction === 'up') {
       
   245                 $this->outputWriter->write("\n" . sprintf('  <info>++</info> migrating <comment>%s</comment>', $this->version) . "\n");
       
   246             } else {
       
   247                 $this->outputWriter->write("\n" . sprintf('  <info>--</info> reverting <comment>%s</comment>', $this->version) . "\n");
       
   248             }
       
   249 
       
   250             $this->state = self::STATE_EXEC;
       
   251 
       
   252             $toSchema = clone $fromSchema;
       
   253             $this->migration->$direction($toSchema);
       
   254             $this->addSql($fromSchema->getMigrateToSql($toSchema, $this->platform));
       
   255 
       
   256             if ($dryRun === false) {
       
   257                 if ($this->sql) {
       
   258                     foreach ($this->sql as $key => $query) {
       
   259                         if ( ! isset($this->params[$key])) {
       
   260                             $this->outputWriter->write('     <comment>-></comment> ' . $query);
       
   261                             $this->connection->executeQuery($query);
       
   262                         } else {
       
   263                             $this->outputWriter->write(sprintf('    <comment>-</comment> %s (with parameters)', $query));
       
   264                             $this->connection->executeQuery($query, $this->params[$key], $this->types[$key]);
       
   265                         }
       
   266                     }
       
   267                 } else {
       
   268                     $this->outputWriter->write(sprintf('<error>Migration %s was executed but did not result in any SQL statements.</error>', $this->version));
       
   269                 }
       
   270 
       
   271                 if ($direction === 'up') {
       
   272                     $this->markMigrated();
       
   273                 } else {
       
   274                     $this->markNotMigrated();
       
   275                 }
       
   276 
       
   277             } else {
       
   278                 foreach ($this->sql as $query) {
       
   279                     $this->outputWriter->write('     <comment>-></comment> ' . $query);
       
   280                 }
       
   281             }
       
   282 
       
   283             $this->state = self::STATE_POST;
       
   284             $this->migration->{'post' . ucfirst($direction)}($toSchema);
       
   285 
       
   286             $end = microtime(true);
       
   287             $this->time = round($end - $start, 2);
       
   288             if ($direction === 'up') {
       
   289                 $this->outputWriter->write(sprintf("\n  <info>++</info> migrated (%ss)", $this->time));
       
   290             } else {
       
   291                 $this->outputWriter->write(sprintf("\n  <info>--</info> reverted (%ss)", $this->time));
       
   292             }
       
   293 
       
   294             $this->connection->commit();
       
   295 
       
   296             return $this->sql;
       
   297         } catch(SkipMigrationException $e) {
       
   298             $this->connection->rollback();
       
   299 
       
   300             if ($dryRun == false) {
       
   301                 // now mark it as migrated
       
   302                 if ($direction === 'up') {
       
   303                     $this->markMigrated();
       
   304                 } else {
       
   305                     $this->markNotMigrated();
       
   306                 }
       
   307             }
       
   308 
       
   309             $this->outputWriter->write(sprintf("\n  <info>SS</info> skipped (Reason: %s)",  $e->getMessage()));
       
   310         } catch (\Exception $e) {
       
   311 
       
   312             $this->outputWriter->write(sprintf(
       
   313                 '<error>Migration %s failed during %s. Error %s</error>',
       
   314                 $this->version, $this->getExecutionState(), $e->getMessage()
       
   315             ));
       
   316 
       
   317             $this->connection->rollback();
       
   318 
       
   319             $this->state = self::STATE_NONE;
       
   320             throw $e;
       
   321         }
       
   322         $this->state = self::STATE_NONE;
       
   323     }
       
   324 
       
   325     public function getExecutionState()
       
   326     {
       
   327         switch($this->state) {
       
   328             case self::STATE_PRE:
       
   329                 return 'Pre-Checks';
       
   330             case self::STATE_POST:
       
   331                 return 'Post-Checks';
       
   332             case self::STATE_EXEC:
       
   333                 return 'Execution';
       
   334             default:
       
   335                 return 'No State';
       
   336         }
       
   337     }
       
   338 
       
   339     /**
       
   340      * Returns the time this migration version took to execute
       
   341      *
       
   342      * @return integer $time The time this migration version took to execute
       
   343      */
       
   344     public function getTime()
       
   345     {
       
   346         return $this->time;
       
   347     }
       
   348 
       
   349     public function __toString()
       
   350     {
       
   351         return $this->version;
       
   352     }
       
   353 }