Add static resources

Yannick Warnier 10 years ago
parent 185ca46d42
commit bfc536942e
  1. 1
      vendor/doctrine/migrations
  2. 1
      vendor/doctrine/migrations/.coveralls.yml
  3. 108
      vendor/doctrine/migrations/README.markdown
  4. 39
      vendor/doctrine/migrations/UPGRADE-1.0.MD
  5. 3
      vendor/doctrine/migrations/bin/doctrine-migrations
  6. 93
      vendor/doctrine/migrations/bin/doctrine-migrations.php
  7. 7
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbortMigrationException.php
  8. 189
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbstractMigration.php
  9. 94
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/AbstractFileConfiguration.php
  10. 695
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Configuration.php
  11. 58
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XmlConfiguration.php
  12. 61
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/YamlConfiguration.php
  13. 65
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/AbstractFinder.php
  14. 45
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/GlobFinder.php
  15. 40
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationFinderInterface.php
  16. 77
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/RecursiveRegexFinder.php
  17. 33
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/IrreversibleMigrationException.php
  18. 173
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Migration.php
  19. 66
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationException.php
  20. 45
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationsVersion.php
  21. 52
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/OutputWriter.php
  22. 82
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/OrmSchemaProvider.php
  23. 36
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaProviderInterface.php
  24. 48
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/StubSchemaProvider.php
  25. 7
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SkipMigrationException.php
  26. 121
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SqlFileWriter.php
  27. 181
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/AbstractCommand.php
  28. 167
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/DiffCommand.php
  29. 102
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/ExecuteCommand.php
  30. 135
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/GenerateCommand.php
  31. 48
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/LatestCommand.php
  32. 192
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/MigrateCommand.php
  33. 140
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/StatusCommand.php
  34. 177
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/VersionCommand.php
  35. 107
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/ConfigurationHelper.php
  36. 392
      vendor/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Version.php
  37. 37
      vendor/doctrine/migrations/phpunit.xml.dist

@ -1 +0,0 @@
Subproject commit 6c527d8ccbccec5257bb2c92bca973653b1e3976

@ -0,0 +1,108 @@
# Doctrine Database Migrations
## Status
[![Build Status](https://travis-ci.org/doctrine/migrations.svg)](https://travis-ci.org/doctrine/migrations)
[![Dependency Status](https://www.versioneye.com/php/doctrine:migrations/badge.svg)](https://www.versioneye.com/php/doctrine:migrations/)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/migrations/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/migrations/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/migrations/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/migrations/?branch=master)
## Official Documentation
All available documentation can be found [here](http://docs.doctrine-project.org/projects/doctrine-migrations/en/latest/).
The repository containing the documentation is [there](https://github.com/doctrine/migrations-documentation).
## Working with Doctrine Migrations
### Using the integration of your framework
* symfony 2 [doctrine/doctrine-migrations-bundle](https://packagist.org/packages/doctrine/doctrine-migrations-bundle)
* ZF2 [doctrine/doctrine-orm-module](https://packagist.org/packages/doctrine/doctrine-orm-module)
* laravel [mitchellvanw/laravel-doctrine](https://packagist.org/packages/mitchellvanw/laravel-doctrine)
* Silex [kurl/silex-doctrine-migrations-provider](https://packagist.org/packages/kurl/silex-doctrine-migrations-provider)
* nette [zenify/doctrine-migrations](https://packagist.org/packages/zenify/doctrine-migrations)
* others...
### Using composer
```composer require doctrine/migrations```
### Downloading the latest phar release
You can download the [doctrine migrations phar](https://github.com/doctrine/migrations/releases) directly on the release page
### Building Your own Phar
Make sure Composer and all necessary dependencies are installed:
```bash
curl -s https://getcomposer.org/installer | php
php composer.phar install --dev
```
Make sure that the Box project is installed:
```bash
curl -s http://box-project.org/installer.php | php
```
Build the PHAR archive:
```bash
php box.phar build
```
The `doctrine-migrations.phar` archive is built in the `build` directory.
#### Creating archive disabled by INI setting
If you receive an error that looks like:
creating archive "build/doctrine-migrations.phar" disabled by INI setting
This can be fixed by setting the following in your php.ini:
```ini
; http://php.net/phar.readonly
phar.readonly = Off
```
## Installing Dependencies
To install dependencies run a composer update:
```composer update```
## symfony 2.3 users
Doctrine migration need the doctrine/orm 2.4, you need to [update your composer.json](https://github.com/symfony/symfony-standard/blob/v2.3.28/composer.json#L12) to the last version of it for symfony 2.3.
That version is compatible with the doctrine/orm 2.4 and there are [very little upgrade needed](https://github.com/doctrine/doctrine2/blob/master/UPGRADE.md#upgrade-to-24).
## Running the unit tests
To run the tests, you need the sqlite extension for php.
On Unix-like systems, install:
- php5-sqlite
On Windows, enable the extension by uncommenting the following lines in php.ini
```
extension = php_pdo_sqlite.dll
extension = php_sqlite3.dll
extension_dir = ext
```
Running the tests from the project root:
```
./vendor/bin/phpunit
```
On Windows run phpunit from the full path
```
php vendor/phpunit/phpunit/phpunit
```
This appears to be some bug.
Happy testing :-)

@ -0,0 +1,39 @@
UPGRADE FROM 1.0-alpha1 to 1.0.0-alpha3
=======================================
## AbstractMigration
### Before:
The method getName() was defined and it's implementation would change the order in which the migration would be processed.
It would cause discrepencies between the file order in a file browser and the order of execution of the migrations.
### After:
The getName method as been removed | set final and new getDescription method has been added.
The goal of this method is to be able to provide context for the migration.
This context is shown for the last migrated migration when the status command is called.
## --write-sql option from the migrate command
### Before:
The --write-sql option would only output sql contained in the migration and would not update the table containing the migrated migrations.
### After:
That option now also output the sql queries necessary to update the table containing the state of the migrations.
If you want to go back to the previous behavior just make a request on the bug tracker as for now the need for it is not very clear.
## MigrationsVersion::VERSION
### Before:
MigrationsVersion::VERSION used to be a property.
The returned value was fanciful.
### After:
It is now a a function so that a different value can be automatically send back if it's a modified version that's used.
The returned value is now the git tag.
The tag is in lowercase as the other doctrine projects.

@ -0,0 +1,3 @@
#!/usr/bin/env php
<?php
include('doctrine-migrations.php');

@ -0,0 +1,93 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
$autoloadFiles = array(
__DIR__ . '/../vendor/autoload.php',
__DIR__ . '/../../../autoload.php'
);
$autoloader = false;
foreach ($autoloadFiles as $autoloadFile) {
if (file_exists($autoloadFile)) {
require_once $autoloadFile;
$autoloader = true;
}
}
if (!$autoloader) {
if (extension_loaded('phar') && ($uri = Phar::running())) {
echo 'The phar has been builded without the depedencies' . PHP_EOL;
}
die('vendor/autoload.php could not be found. Did you run `php composer.phar install`?');
}
// Support for using the Doctrine ORM convention of providing a `cli-config.php` file.
$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php';
$helperSet = null;
if (file_exists($configFile)) {
if ( ! is_readable($configFile)) {
trigger_error(
'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR
);
}
require $configFile;
foreach ($GLOBALS as $helperSetCandidate) {
if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) {
$helperSet = $helperSetCandidate;
break;
}
}
}
$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet();
if(class_exists('\Symfony\Component\Console\Helper\QuestionHelper')) {
$helperSet->set(new \Symfony\Component\Console\Helper\QuestionHelper(), 'question');
} else {
$helperSet->set(new \Symfony\Component\Console\Helper\DialogHelper(), 'dialog');
}
$cli = new \Symfony\Component\Console\Application('Doctrine Migrations', \Doctrine\DBAL\Migrations\MigrationsVersion::VERSION());
$cli->setCatchExceptions(true);
$cli->setHelperSet($helperSet);
$cli->addCommands(array(
// Migrations Commands
new \Doctrine\DBAL\Migrations\Tools\Console\Command\ExecuteCommand(),
new \Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand(),
new \Doctrine\DBAL\Migrations\Tools\Console\Command\LatestCommand(),
new \Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand(),
new \Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand(),
new \Doctrine\DBAL\Migrations\Tools\Console\Command\VersionCommand()
));
if ($helperSet->has('em')) {
$cli->add(new \Doctrine\DBAL\Migrations\Tools\Console\Command\DiffCommand());
}
$input = file_exists('migrations-input.php')
? include 'migrations-input.php' : null;
$output = file_exists('migrations-output.php')
? include 'migrations-output.php' : null;
$cli->run($input, $output);

@ -0,0 +1,7 @@
<?php
namespace Doctrine\DBAL\Migrations;
class AbortMigrationException extends MigrationException
{
}

@ -0,0 +1,189 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations;
use Doctrine\DBAL\Schema\Schema;
/**
* Abstract class for individual migrations to extend from.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
abstract class AbstractMigration
{
/**
* Reference to the Version instance representing this migration
*
* @var Version
*/
protected $version;
/**
* The Doctrine\DBAL\Connection instance we are migrating
*
* @var \Doctrine\DBAL\Connection
*/
protected $connection;
/**
* Reference to the SchemaManager instance referenced by $_connection
*
* @var \Doctrine\DBAL\Schema\AbstractSchemaManager
*/
protected $sm;
/**
* Reference to the DatabasePlatform instance referenced by $_connection
*
* @var \Doctrine\DBAL\Platforms\AbstractPlatform
*/
protected $platform;
/**
* The OutputWriter object instance used for outputting information
*
* @var OutputWriter
*/
private $outputWriter;
public function __construct(Version $version)
{
$config = $version->getConfiguration();
$this->version = $version;
$this->connection = $config->getConnection();
$this->sm = $this->connection->getSchemaManager();
$this->platform = $this->connection->getDatabasePlatform();
$this->outputWriter = $config->getOutputWriter();
}
/**
* Indicates the transactional mode of this migration.
* If this function returns true (default) the migration will be executed in one transaction,
* otherwise non-transactional state will be used to execute each of the migration SQLs.
*
* Extending class should override this function to alter the return value
*
* @return bool TRUE by default.
*/
public function isTransactional()
{
return true;
}
/**
* Get migration description
*
* @return string
*/
public function getDescription()
{
return '';
}
/**
* Print a warning message if the condition evaluates to TRUE.
*
* @param boolean $condition
* @param string $message
*/
public function warnIf($condition, $message = '')
{
if ($condition) {
$message = $message?: 'Unknown Reason';
$this->outputWriter->write(sprintf(
' <warning>Warning during %s: %s</warning>',
$this->version->getExecutionState(),
$message
));
}
}
/**
* Abort the migration if the condition evaluates to TRUE.
*
* @param boolean $condition
* @param string $message
*
* @throws AbortMigrationException
*/
public function abortIf($condition, $message = '')
{
if ($condition) {
throw new AbortMigrationException($message ?: 'Unknown Reason');
}
}
/**
* Skip this migration (but not the next ones) if condition evaluates to TRUE.
*
* @param boolean $condition
* @param string $message
*
* @throws SkipMigrationException
*/
public function skipIf($condition, $message = '')
{
if ($condition) {
throw new SkipMigrationException($message ?: 'Unknown Reason');
}
}
public function preUp(Schema $schema)
{
}
public function postUp(Schema $schema)
{
}
public function preDown(Schema $schema)
{
}
public function postDown(Schema $schema)
{
}
abstract public function up(Schema $schema);
abstract public function down(Schema $schema);
protected function addSql($sql, array $params = array(), array $types = array())
{
$this->version->addSql($sql, $params, $types);
}
protected function write($message)
{
$this->outputWriter->write($message);
}
protected function throwIrreversibleMigrationException($message = null)
{
if (null === $message) {
$message = 'This migration is irreversible and cannot be reverted.';
}
throw new IrreversibleMigrationException($message);
}
}

@ -0,0 +1,94 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Configuration;
use Doctrine\DBAL\Migrations\MigrationException;
/**
* Abstract Migration Configuration class for loading configuration information
* from a configuration file (xml or yml).
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
abstract class AbstractFileConfiguration extends Configuration
{
/**
* The configuration file used to load configuration information
*
* @var string
*/
private $file;
/**
* Whether or not the configuration file has been loaded yet or not
*
* @var boolean
*/
private $loaded = false;
/**
* Load the information from the passed configuration file
*
* @param string $file The path to the configuration file
*
* @throws MigrationException Throws exception if configuration file was already loaded
*/
public function load($file)
{
if ($this->loaded) {
throw MigrationException::configurationFileAlreadyLoaded();
}
if (file_exists($path = getcwd() . '/' . $file)) {
$file = $path;
}
$this->file = $file;
$this->doLoad($file);
$this->loaded = true;
}
protected function getDirectoryRelativeToFile($file, $input)
{
$path = realpath(dirname($file) . '/' . $input);
if ($path !== false) {
$directory = $path;
} else {
$directory = $input;
}
return $directory;
}
public function getFile()
{
return $this->file;
}
/**
* Abstract method that each file configuration driver must implement to
* load the given configuration file whether it be xml, yaml, etc. or something
* else.
*
* @param string $file The path to a configuration file.
*/
abstract protected function doLoad($file);
}

@ -0,0 +1,695 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Migrations\MigrationException;
use Doctrine\DBAL\Migrations\OutputWriter;
use Doctrine\DBAL\Migrations\Version;
use Doctrine\DBAL\Migrations\Finder\MigrationFinderInterface;
use Doctrine\DBAL\Migrations\Finder\RecursiveRegexFinder;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Type;
/**
* Default Migration Configuration object used for configuring an instance of
* the Migration class. Set the connection, version table name, register migration
* classes/versions, etc.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class Configuration
{
/**
* Name of this set of migrations
*
* @var string
*/
private $name;
/**
* Flag for whether or not the migration table has been created
*
* @var boolean
*/
private $migrationTableCreated = false;
/**
* Connection instance to use for migrations
*
* @var Connection
*/
private $connection;
/**
* OutputWriter instance for writing output during migrations
*
* @var OutputWriter
*/
private $outputWriter;
/**
* The migration finder implementation -- used to load migrations from a
* directory.
*
* @var MigrationFinderInterface
*/
private $migrationFinder;
/**
* The migration table name to track versions in
*
* @var string
*/
private $migrationsTableName = 'doctrine_migration_versions';
/**
* The path to a directory where new migration classes will be written
*
* @var string
*/
private $migrationsDirectory;
/**
* Namespace the migration classes live in
*
* @var string
*/
private $migrationsNamespace;
/**
* Array of the registered migrations
*
* @var Version[]
*/
private $migrations = array();
/**
* Construct a migration configuration object.
*
* @param Connection $connection A Connection instance
* @param OutputWriter $outputWriter A OutputWriter instance
* @param MigrationFinderInterface $finder Migration files finder
*/
public function __construct(Connection $connection, OutputWriter $outputWriter = null, MigrationFinderInterface $finder = null)
{
$this->connection = $connection;
if ($outputWriter === null) {
$outputWriter = new OutputWriter();
}
$this->outputWriter = $outputWriter;
$this->migrationFinder = $finder;
}
/**
* Validation that this instance has all the required properties configured
*
* @throws MigrationException
*/
public function validate()
{
if (! $this->migrationsNamespace) {
throw MigrationException::migrationsNamespaceRequired();
}
if (! $this->migrationsDirectory) {
throw MigrationException::migrationsDirectoryRequired();
}
}
/**
* Set the name of this set of migrations
*
* @param string $name The name of this set of migrations
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Returns the name of this set of migrations
*
* @return string $name The name of this set of migrations
*/
public function getName()
{
return $this->name;
}
/**
* Returns the OutputWriter instance
*
* @return OutputWriter $outputWriter The OutputWriter instance
*/
public function getOutputWriter()
{
return $this->outputWriter;
}
/**
* Returns a timestamp version as a formatted date
*
* @param string $version
*
* @return string The formatted version
* @deprecated
*/
public function formatVersion($version)
{
return $this->getDateTime($version);
}
/**
* Returns the datetime of a migration
*
* @param $version
* @return string
*/
public function getDateTime($version)
{
$datetime = str_replace('Version', '', $version);
$datetime = \DateTime::createFromFormat('YmdHis', $datetime);
if ($datetime === false){
return '';
}
return $datetime->format('Y-m-d H:i:s');
}
/**
* Returns the Connection instance
*
* @return Connection $connection The Connection instance
*/
public function getConnection()
{
return $this->connection;
}
/**
* Set the migration table name
*
* @param string $tableName The migration table name
*/
public function setMigrationsTableName($tableName)
{
$this->migrationsTableName = $tableName;
}
/**
* Returns the migration table name
*
* @return string $migrationsTableName The migration table name
*/
public function getMigrationsTableName()
{
return $this->migrationsTableName;
}
/**
* Set the new migrations directory where new migration classes are generated
*
* @param string $migrationsDirectory The new migrations directory
*/
public function setMigrationsDirectory($migrationsDirectory)
{
$this->migrationsDirectory = $migrationsDirectory;
}
/**
* Returns the new migrations directory where new migration classes are generated
*
* @return string $migrationsDirectory The new migrations directory
*/
public function getMigrationsDirectory()
{
return $this->migrationsDirectory;
}
/**
* Set the migrations namespace
*
* @param string $migrationsNamespace The migrations namespace
*/
public function setMigrationsNamespace($migrationsNamespace)
{
$this->migrationsNamespace = $migrationsNamespace;
}
/**
* Returns the migrations namespace
*
* @return string $migrationsNamespace The migrations namespace
*/
public function getMigrationsNamespace()
{
return $this->migrationsNamespace;
}
/**
* set the implementation of the migration finder.
*
* @param $finder The new migration finder
* @return void
*/
public function setMigrationFinder(MigrationFinderInterface $finder)
{
$this->migrationFinder = $finder;
}
/**
* Register migrations from a given directory. Recursively finds all files
* with the pattern VersionYYYYMMDDHHMMSS.php as the filename and registers
* them as migrations.
*
* @param string $path The root directory to where some migration classes live.
*
* @return Version[] The array of migrations registered.
*/
public function registerMigrationsFromDirectory($path)
{
return $this->registerMigrations($this->findMigrations($path));
}
/**
* Register a single migration version to be executed by a AbstractMigration
* class.
*
* @param string $version The version of the migration in the format YYYYMMDDHHMMSS.
* @param string $class The migration class to execute for the version.
*
* @return Version
*
* @throws MigrationException
*/
public function registerMigration($version, $class)
{
$version = (string) $version;
$class = (string) $class;
if (isset($this->migrations[$version])) {
throw MigrationException::duplicateMigrationVersion($version, get_class($this->migrations[$version]));
}
$version = new Version($this, $version, $class);
$this->migrations[$version->getVersion()] = $version;
ksort($this->migrations);
return $version;
}
/**
* Register an array of migrations. Each key of the array is the version and
* the value is the migration class name.
*
*
* @param array $migrations
*
* @return Version[]
*/
public function registerMigrations(array $migrations)
{
$versions = array();
foreach ($migrations as $version => $class) {
$versions[] = $this->registerMigration($version, $class);
}
return $versions;
}
/**
* Get the array of registered migration versions.
*
* @return Version[] $migrations
*/
public function getMigrations()
{
return $this->migrations;
}
/**
* Returns the Version instance for a given version in the format YYYYMMDDHHMMSS.
*
* @param string $version The version string in the format YYYYMMDDHHMMSS.
*
* @return Version
*
* @throws MigrationException Throws exception if migration version does not exist.
*/
public function getVersion($version)
{
if ( ! isset($this->migrations[$version])) {
throw MigrationException::unknownMigrationVersion($version);
}
return $this->migrations[$version];
}
/**
* Check if a version exists.
*
* @param string $version
*
* @return boolean
*/
public function hasVersion($version)
{
return isset($this->migrations[$version]);
}
/**
* Check if a version has been migrated or not yet
*
* @param Version $version
*
* @return boolean
*/
public function hasVersionMigrated(Version $version)
{
$this->createMigrationTable();
$version = $this->connection->fetchColumn(
"SELECT version FROM " . $this->migrationsTableName . " WHERE version = ?",
array($version->getVersion())
);
return $version !== false;
}
/**
* Returns all migrated versions from the versions table, in an array.
*
* @return Version[]
*/
public function getMigratedVersions()
{
$this->createMigrationTable();
$ret = $this->connection->fetchAll("SELECT version FROM " . $this->migrationsTableName);
$versions = array();
foreach ($ret as $version) {
$versions[] = current($version);
}
return $versions;
}
/**
* Returns an array of available migration version numbers.
*
* @return array
*/
public function getAvailableVersions()
{
$availableVersions = array();
foreach ($this->migrations as $migration) {
$availableVersions[] = $migration->getVersion();
}
return $availableVersions;
}
/**
* Returns the current migrated version from the versions table.
*
* @return string
*/
public function getCurrentVersion()
{
$this->createMigrationTable();
$where = null;
if (!empty($this->migrations)) {
$migratedVersions = array();
foreach ($this->migrations as $migration) {
$migratedVersions[] = sprintf("'%s'", $migration->getVersion());
}
$where = " WHERE version IN (" . implode(', ', $migratedVersions) . ")";
}
$sql = sprintf("SELECT version FROM %s%s ORDER BY version DESC",
$this->migrationsTableName, $where
);
$sql = $this->connection->getDatabasePlatform()->modifyLimitQuery($sql, 1);
$result = $this->connection->fetchColumn($sql);
return $result !== false ? (string) $result : '0';
}
/**
* Returns the version prior to the current version.
*
* @return string|null A version string, or null if the current version is
* the first.
*/
public function getPrevVersion()
{
return $this->getRelativeVersion($this->getCurrentVersion(), -1);
}
/**
* Returns the version following the current version.
*
* @return string|null A version string, or null if the current version is
* the latest.
*/
public function getNextVersion()
{
return $this->getRelativeVersion($this->getCurrentVersion(), 1);
}
/**
* Returns the version with the specified offset to the specified version.
*
* @return string|null A version string, or null if the specified version
* is unknown or the specified delta is not within the
* list of available versions.
*/
public function getRelativeVersion($version, $delta)
{
$versions = array_keys($this->migrations);
array_unshift($versions, 0);
$offset = array_search($version, $versions);
if ($offset === false || !isset($versions[$offset + $delta])) {
// Unknown version or delta out of bounds.
return null;
}
return (string) $versions[$offset + $delta];
}
/**
* Returns the version number from an alias.
*
* Supported aliases are:
* - first: The very first version before any migrations have been run.
* - current: The current version.
* - prev: The version prior to the current version.
* - next: The version following the current version.
* - latest: The latest available version.
*
* If an existing version number is specified, it is returned verbatimly.
*
* @return string|null A version number, or null if the specified alias
* does not map to an existing version, e.g. if "next"
* is passed but the current version is already the
* latest.
*/
public function resolveVersionAlias($alias)
{
if ($this->hasVersion($alias)) {
return $alias;
}
switch ($alias) {
case 'first':
return '0';
case 'current':
return $this->getCurrentVersion();
case 'prev':
return $this->getPrevVersion();
case 'next':
return $this->getNextVersion();
case 'latest':
return $this->getLatestVersion();
default:
return null;
}
}
/**
* Returns the total number of executed migration versions
*
* @return integer
*/
public function getNumberOfExecutedMigrations()
{
$this->createMigrationTable();
$result = $this->connection->fetchColumn("SELECT COUNT(version) FROM " . $this->migrationsTableName);
return $result !== false ? $result : 0;
}
/**
* Returns the total number of available migration versions
*
* @return integer
*/
public function getNumberOfAvailableMigrations()
{
return count($this->migrations);
}
/**
* Returns the latest available migration version.
*
* @return string The version string in the format YYYYMMDDHHMMSS.
*/
public function getLatestVersion()
{
$versions = array_keys($this->migrations);
$latest = end($versions);
return $latest !== false ? (string) $latest : '0';
}
/**
* Create the migration table to track migrations with.
*
* @return boolean Whether or not the table was created.
*/
public function createMigrationTable()
{
$this->validate();
if ($this->migrationTableCreated) {
return false;
}
if ( ! $this->connection->getSchemaManager()->tablesExist(array($this->migrationsTableName))) {
$columns = array(
'version' => new Column('version', Type::getType('string'), array('length' => 255)),
);
$table = new Table($this->migrationsTableName, $columns);
$table->setPrimaryKey(array('version'));
$this->connection->getSchemaManager()->createTable($table);
$this->migrationTableCreated = true;
return true;
}
$this->migrationTableCreated = true;
return false;
}
/**
* Returns the array of migrations to executed based on the given direction
* and target version number.
*
* @param string $direction The direction we are migrating.
* @param string $to The version to migrate to.
*
* @return Version[] $migrations The array of migrations we can execute.
*/
public function getMigrationsToExecute($direction, $to)
{
if ($direction === 'down') {
if (count($this->migrations)) {
$allVersions = array_reverse(array_keys($this->migrations));
$classes = array_reverse(array_values($this->migrations));
$allVersions = array_combine($allVersions, $classes);
} else {
$allVersions = array();
}
} else {
$allVersions = $this->migrations;
}
$versions = array();
$migrated = $this->getMigratedVersions();
foreach ($allVersions as $version) {
if ($this->shouldExecuteMigration($direction, $version, $to, $migrated)) {
$versions[$version->getVersion()] = $version;
}
}
return $versions;
}
/**
* Find all the migrations in a given directory.
*
* @param string $path the directory to search.
* @return array
*/
protected function findMigrations($path)
{
return $this->getMigrationFinder()->findMigrations($path, $this->getMigrationsNamespace());
}
/**
* Get the migration finder, creating one if it's not present.
*
* @return MigrationFinderInterface
*/
protected function getMigrationFinder()
{
if (!$this->migrationFinder) {
$this->migrationFinder = new RecursiveRegexFinder();
}
return $this->migrationFinder;
}
/**
* Check if we should execute a migration for a given direction and target
* migration version.
*
* @param string $direction The direction we are migrating.
* @param Version $version The Version instance to check.
* @param string $to The version we are migrating to.
* @param array $migrated Migrated versions array.
*
* @return boolean
*/
private function shouldExecuteMigration($direction, Version $version, $to, $migrated)
{
if ($direction === 'down') {
if ( ! in_array($version->getVersion(), $migrated)) {
return false;
}
return $version->getVersion() > $to;
}
if ($direction === 'up') {
if (in_array($version->getVersion(), $migrated)) {
return false;
}
return $version->getVersion() <= $to;
}
}
}

@ -0,0 +1,58 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Configuration;
/**
* Load migration configuration information from a XML configuration file.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class XmlConfiguration extends AbstractFileConfiguration
{
/**
* @inheritdoc
*/
protected function doLoad($file)
{
$xml = simplexml_load_file($file);
if (isset($xml->name)) {
$this->setName((string) $xml->name);
}
if (isset($xml->table['name'])) {
$this->setMigrationsTableName((string) $xml->table['name']);
}
if (isset($xml->{'migrations-namespace'})) {
$this->setMigrationsNamespace((string) $xml->{'migrations-namespace'});
}
if (isset($xml->{'migrations-directory'})) {
$migrationsDirectory = $this->getDirectoryRelativeToFile($file, (string) $xml->{'migrations-directory'});
$this->setMigrationsDirectory($migrationsDirectory);
$this->registerMigrationsFromDirectory($migrationsDirectory);
}
if (isset($xml->migrations->migration)) {
foreach ($xml->migrations->migration as $migration) {
$this->registerMigration((string) $migration['version'], (string) $migration['class']);
}
}
}
}

@ -0,0 +1,61 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Configuration;
use Symfony\Component\Yaml\Yaml;
/**
* Load migration configuration information from a YAML configuration file.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class YamlConfiguration extends AbstractFileConfiguration
{
/**
* @inheritdoc
*/
protected function doLoad($file)
{
$array = Yaml::parse(file_get_contents($file));
if (isset($array['name'])) {
$this->setName($array['name']);
}
if (isset($array['table_name'])) {
$this->setMigrationsTableName($array['table_name']);
}
if (isset($array['migrations_namespace'])) {
$this->setMigrationsNamespace($array['migrations_namespace']);
}
if (isset($array['migrations_directory'])) {
$migrationsDirectory = $this->getDirectoryRelativeToFile($file, $array['migrations_directory']);
$this->setMigrationsDirectory($migrationsDirectory);
$this->registerMigrationsFromDirectory($migrationsDirectory);
}
if (isset($array['migrations']) && is_array($array['migrations'])) {
foreach ($array['migrations'] as $migration) {
$this->registerMigration($migration['version'], $migration['class']);
}
}
}
}

@ -0,0 +1,65 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Finder;
/**
* Abstract base class for MigrationFinders
*
* @since 1.0.0-alpha3
*/
abstract class AbstractFinder implements MigrationFinderInterface
{
protected static function requireOnce($path)
{
require_once $path;
}
protected function getRealPath($directory)
{
$dir = realpath($directory);
if (false === $dir || !is_dir($dir)) {
throw new \InvalidArgumentException(sprintf(
'Cannot load migrations from "%s" because it is not a valid directory',
$directory
));
}
return $dir;
}
/**
* Load the migrations and return an array of thoses loaded migrations
* @param $files array of migration filename found
* @param $namespace namespace of thoses migrations
* @return array constructed with the migration name as key and the value is the fully qualified name of the migration
*/
protected function loadMigrations($files, $namespace)
{
$migrations = [];
foreach ($files as $file) {
static::requireOnce($file);
$className = basename($file, '.php');
$version = substr($className, 7);
$migrations[$version] = sprintf('%s\\%s', $namespace, $className);
}
return $migrations;
}
}

@ -0,0 +1,45 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Finder;
/**
* A MigrationFinderInterface implementation that uses `glob` and some special file and
* class names to load migrations from a directory.
*
* The migrations are expected to reside in files with the filename
* `VersionYYYYMMDDHHMMSS.php`. Each file should contain one class named
* `VersionYYYYMMDDHHMMSS`.
*
* @since 1.0.0-alpha3
*/
final class GlobFinder extends AbstractFinder
{
/**
* {@inheritdoc}
*/
public function findMigrations($directory, $namespace=null)
{
$dir = $this->getRealPath($directory);
$files = glob(rtrim($dir, '/').'/Version*.php');
return $this->loadMigrations($files, $namespace);
}
}

@ -0,0 +1,40 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Finder;
/**
* MigrationFinderInterface implementations locate migrations (classes that extend
* `Doctrine\DBAL\Migrations\AbstractMigration`) in a directory.
*
* @since 1.0.0-alpha3
*/
interface MigrationFinderInterface
{
/**
* Find all the migrations in a directory for the given path and namespace.
*
* @param string $directory The directory in which to look for migrations
* @param string|null $namespace The namespace of the classes to load
* @throws InvalidArgumentException if the directory does not exist
* @return string[] An array of class names that were found with the version
* as keys.
*/
public function findMigrations($directory, $namespace=null);
}

@ -0,0 +1,77 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Finder;
/**
* A MigrationFinderInterface implementation that uses a RegexIterator along with a
* RecursiveDirectoryIterator.
*
* @since 1.0.0-alpha3
*/
final class RecursiveRegexFinder extends AbstractFinder
{
/**
* {@inheritdoc}
*/
public function findMigrations($directory, $namespace=null)
{
$dir = $this->getRealPath($directory);
return $this->loadMigrations($this->getMatches($this->createIterator($dir)), $namespace);
}
/**
* Create a recursive iterator to find all the migrations in the subdirectories.
* @param $dir
* @return \RegexIterator
*/
private function createIterator($dir)
{
return new \RegexIterator(
new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::LEAVES_ONLY
),
$this->getPattern(),
\RegexIterator::GET_MATCH
);
}
private function getPattern()
{
return sprintf('#^.+\\%sVersion[^\\%s]{1,255}\\.php$#i', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR);
}
/**
* Transform the recursiveIterator result array of array into the expected array of migration file
* @param $iteratorFilesMatch
* @return array
*/
private function getMatches($iteratorFilesMatch)
{
$files = [];
foreach($iteratorFilesMatch as $file) {
$files[] = $file[0];
}
return $files;
}
}

@ -0,0 +1,33 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations;
/**
* Exception to be thrown in the down() methods of migrations that signifies it
* is an irreversible migration and stops execution.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class IrreversibleMigrationException extends \Exception
{
}

@ -0,0 +1,173 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations;
use Doctrine\DBAL\Migrations\Configuration\Configuration;
/**
* Class for running migrations to the current version or a manually specified version.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class Migration
{
/**
* The OutputWriter object instance used for outputting information
*
* @var OutputWriter
*/
private $outputWriter;
/**
* @var Configuration
*/
private $configuration;
/**
* Construct a Migration instance
*
* @param Configuration $configuration A migration Configuration instance
*/
public function __construct(Configuration $configuration)
{
$this->configuration = $configuration;
$this->outputWriter = $configuration->getOutputWriter();
}
/**
* Get the array of versions and SQL queries that would be executed for
* each version but do not execute anything.
*
* @param string $to The version to migrate to.
*
* @return array $sql The array of SQL queries.
*/
public function getSql($to = null)
{
return $this->migrate($to, true);
}
/**
* Write a migration SQL file to the given path
*
* @param string $path The path to write the migration SQL file.
* @param string $to The version to migrate to.
*
* @return boolean $written
*/
public function writeSqlFile($path, $to = null)
{
$sql = $this->getSql($to);
$from = $this->configuration->getCurrentVersion();
if ($to === null) {
$to = $this->configuration->getLatestVersion();
}
$direction = $from > $to ? 'down' : 'up';
$this->outputWriter->write(sprintf("# Migrating from %s to %s\n", $from, $to));
$sqlWriter = new SqlFileWriter(
$this->configuration->getMigrationsTableName(),
$path,
$this->outputWriter
);
return $sqlWriter->write($sql, $direction);
}
/**
* Run a migration to the current version or the given target version.
*
* @param string $to The version to migrate to.
* @param boolean $dryRun Whether or not to make this a dry run and not execute anything.
* @param boolean $timeAllQueries Measuring or not the execution time of each SQL query.
*
* @return array $sql The array of migration sql statements
*
* @throws MigrationException
*/
public function migrate($to = null, $dryRun = false, $timeAllQueries = false)
{
/**
* If no version to migrate to is given we default to the last available one.
*/
if ($to === null) {
$to = $this->configuration->getLatestVersion();
}
$from = (string) $this->configuration->getCurrentVersion();
$to = (string) $to;
/**
* Throw an error if we can't find the migration to migrate to in the registered
* migrations.
*/
$migrations = $this->configuration->getMigrations();
if ( ! isset($migrations[$to]) && $to > 0) {
throw MigrationException::unknownMigrationVersion($to);
}
$direction = $from > $to ? 'down' : 'up';
$migrationsToExecute = $this->configuration->getMigrationsToExecute($direction, $to);
/**
* If
* there are no migrations to execute
* and there are migrations,
* and the migration from and to are the same
* means we are already at the destination return an empty array()
* to signify that there is nothing left to do.
*/
if ($from === $to && empty($migrationsToExecute) && !empty($migrations)) {
return array();
}
$output = $dryRun ? 'Executing dry run of migration' : 'Migrating';
$output .= ' <info>%s</info> to <comment>%s</comment> from <comment>%s</comment>';
$this->outputWriter->write(sprintf($output, $direction, $to, $from));
/**
* If there are no migrations to execute throw an exception.
*/
if (empty($migrationsToExecute)) {
throw MigrationException::noMigrationsToExecute();
}
$sql = array();
$time = 0;
foreach ($migrationsToExecute as $version) {
$versionSql = $version->execute($direction, $dryRun, $timeAllQueries);
$sql[$version->getVersion()] = $versionSql;
$time += $version->getTime();
}
$this->outputWriter->write("\n <comment>------------------------</comment>\n");
$this->outputWriter->write(sprintf(" <info>++</info> finished in %s", $time));
$this->outputWriter->write(sprintf(" <info>++</info> %s migrations executed", count($migrationsToExecute)));
$this->outputWriter->write(sprintf(" <info>++</info> %s sql queries", count($sql, true) - count($sql)));
return $sql;
}
}

@ -0,0 +1,66 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations;
/**
* Class for Migrations specific exceptions
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class MigrationException extends \Exception
{
public static function migrationsNamespaceRequired()
{
return new self('Migrations namespace must be configured in order to use Doctrine migrations.', 2);
}
public static function migrationsDirectoryRequired()
{
return new self('Migrations directory must be configured in order to use Doctrine migrations.', 3);
}
public static function noMigrationsToExecute()
{
return new self('Could not find any migrations to execute.', 4);
}
public static function unknownMigrationVersion($version)
{
return new self(sprintf('Could not find migration version %s', $version), 5);
}
public static function alreadyAtVersion($version)
{
return new self(sprintf('Database is already at version %s', $version), 6);
}
public static function duplicateMigrationVersion($version, $class)
{
return new self(sprintf('Migration version %s already registered with class %s', $version, $class), 7);
}
public static function configurationFileAlreadyLoaded()
{
return new self(sprintf('Migrations configuration file already loaded'), 8);
}
}

@ -0,0 +1,45 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations;
class MigrationsVersion
{
private static $version = 'v1.0.0-alpha3';
public static function VERSION(){
$gitversion = '@git-version@';
if (self::isACustomPharBuild($gitversion)) {
return $gitversion;
}
return self::$version;
}
/**
* @param $gitversion
* @return bool
*
* Check if doctrine migration is installed by composer or
* in a modified (not tagged) phar version.
*/
private static function isACustomPharBuild($gitversion) {
return $gitversion !== '@' . 'git-version@';
}
}

@ -0,0 +1,52 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations;
/**
* Simple class for outputting information from migrations.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class OutputWriter
{
private $closure;
public function __construct(\Closure $closure = null)
{
if ($closure === null) {
$closure = function ($message) {};
}
$this->closure = $closure;
}
/**
* Write output using the configured closure.
*
* @param string $message The message to write.
*/
public function write($message)
{
$closure = $this->closure;
$closure($message);
}
}

@ -0,0 +1,82 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Provider;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Tools\SchemaTool;
/**
* A schema provider that uses the doctrine ORM to generate schemas.
*
* @since 1.0.0-alpha3
*/
final class OrmSchemaProvider implements SchemaProviderInterface
{
/**
* @var EntityManagerInterface
*/
private $entityManager;
public function __construct($em)
{
if (!$this->isEntityManager($em)) {
throw new \InvalidArgumentException(sprintf(
'$em is not a valid Doctrine ORM Entity Manager, got "%s"',
is_object($em) ? get_class($em) : gettype($em)
));
}
$this->entityManager = $em;
}
/**
* {@inheritdoc}
*/
public function createSchema()
{
$metadata = $this->entityManager->getMetadataFactory()->getAllMetadata();
if (empty($metadata)) {
throw new \UnexpectedValueException('No mapping information to process');
}
$tool = new SchemaTool($this->entityManager);
return $tool->getSchemaFromMetadata($metadata);
}
/**
* Doctrine's EntityManagerInterface was introduced in version 2.4, since this
* library allows those older version we need to be able to check for those
* old ORM versions. Hence the helper method.
*
* No need to check to see if EntityManagerInterface exists first here, PHP
* doesn't care.
*
* @param mixed $manager Hopefully an entity manager, but it may be anything
* @return boolean
*/
private function isEntityManager($manager)
{
return $manager instanceof EntityManagerInterface || $manager instanceof EntityManager;
}
}

@ -0,0 +1,36 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Provider;
/**
* Generates `Schema` objects for the diff command. A schema provider should
* return the schema to which the database should be migrated.
*
* @since 1.0.0-alpha3
*/
interface SchemaProviderInterface
{
/**
* Create the schema to which the database should be migrated.
*
* @return \Doctrine\DBAL\Schema\Schema
*/
public function createSchema();
}

@ -0,0 +1,48 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Provider;
use Doctrine\DBAL\Schema\Schema;
/**
* A schemea provider implementation that just returns the schema its given.
*
* @since 1.0.0-alpha3
*/
final class StubSchemaProvider implements SchemaProviderInterface
{
/**
* @var Schema
*/
private $toSchema;
public function __construct(Schema $schema)
{
$this->toSchema = $schema;
}
/**
* {@inheritdoc}
*/
public function createSchema()
{
return $this->toSchema;
}
}

@ -0,0 +1,7 @@
<?php
namespace Doctrine\DBAL\Migrations;
class SkipMigrationException extends MigrationException
{
}

@ -0,0 +1,121 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception\InvalidArgumentException;
class SqlFileWriter
{
private $migrationsTableName;
private $destPath;
/** @var null|OutputWriter */
private $outputWriter;
/**
* @param string $migrationsTableName
* @param string $destPath
* @param \Doctrine\DBAL\Migrations\OutputWriter $outputWriter
*/
public function __construct($migrationsTableName, $destPath, OutputWriter $outputWriter = null)
{
if (empty($migrationsTableName)) {
$this->throwInvalidArgumentException('Migrations table name cannot be empty.');
}
$this->migrationsTableName = $migrationsTableName;
if (empty($destPath)) {
$this->throwInvalidArgumentException('Destination file must be specified.');
}
$this->destPath = $destPath;
$this->outputWriter = $outputWriter;
}
/**
* @param array $queriesByVersion array Keys are versions and values are arrays of SQL queries (they must be castable to string)
* @param string $direction
* @return int|bool
*/
public function write(array $queriesByVersion, $direction)
{
$path = $this->buildMigrationFilePath();
$string = $this->buildMigrationFile($queriesByVersion, $direction);
if ($this->outputWriter) {
$this->outputWriter->write("\n" . sprintf('Writing migration file to "<info>%s</info>"', $path));
}
return file_put_contents($path, $string);
}
private function buildMigrationFile(array $queriesByVersion, $direction)
{
$string = sprintf("# Doctrine Migration File Generated on %s\n", date('Y-m-d H:i:s'));
foreach ($queriesByVersion as $version => $queries) {
$string .= "\n# Version " . $version . "\n";
foreach ($queries as $query) {
$string .= $query . ";\n";
}
$string .= $this->getVersionUpdateQuery($version, $direction);
}
return $string;
}
private function getVersionUpdateQuery($version, $direction)
{
if ($direction == 'down') {
$query = "DELETE FROM %s WHERE version = '%s';\n";
} else {
$query = "INSERT INTO %s (version) VALUES ('%s');\n";
}
return sprintf($query, $this->migrationsTableName, $version);
}
private function buildMigrationFilePath()
{
$path = $this->destPath;
if (is_dir($path)) {
$path = realpath($path);
$path = $path . '/doctrine_migration_' . date('YmdHis') . '.sql';
}
return $path;
}
/**
* This only exists for backwards-compatibiliy with DBAL 2.4
*/
protected function throwInvalidArgumentException($message)
{
if (class_exists('Doctrine\DBAL\Exception\InvalidArgumentException')) {
throw new InvalidArgumentException($message);
} else {
throw new DBALException($message);
}
}
}

@ -0,0 +1,181 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Tools\Console\Command;
use Doctrine\DBAL\Migrations\Configuration\Configuration;
use Doctrine\DBAL\Migrations\OutputWriter;
use Doctrine\DBAL\Migrations\Tools\Console\Helper\ConfigurationHelper;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Question\ConfirmationQuestion;
/**
* CLI Command for adding and deleting migration versions from the version table.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan Wage <jonwage@gmail.com>
*/
abstract class AbstractCommand extends Command
{
/**
* The configuration property only contains the configuration injected by the setter.
*
* @var Configuration
*/
private $configuration;
/**
* The migrationConfiguration property contains the configuration
* created taking into account the command line options.
*
* @var Configuration
*/
private $migrationConfiguration;
/**
* @var OutputWriter
*/
private $outputWriter;
/**
* @var \Doctrine\DBAL\Connection
*/
private $connection;
protected function configure()
{
$this->addOption('configuration', null, InputOption::VALUE_OPTIONAL, 'The path to a migrations configuration file.');
$this->addOption('db-configuration', null, InputOption::VALUE_OPTIONAL, 'The path to a database connection configuration file.');
}
protected function outputHeader(Configuration $configuration, OutputInterface $output)
{
$name = $configuration->getName();
$name = $name ? $name : 'Doctrine Database Migrations';
$name = str_repeat(' ', 20) . $name . str_repeat(' ', 20);
$output->writeln('<question>' . str_repeat(' ', strlen($name)) . '</question>');
$output->writeln('<question>' . $name . '</question>');
$output->writeln('<question>' . str_repeat(' ', strlen($name)) . '</question>');
$output->writeln('');
}
public function setMigrationConfiguration(Configuration $config)
{
$this->configuration = $config;
}
/**
* When any (config) command line option is passed to the migration the migrationConfiguration
* property is set with the new generated configuration.
* If no (config) option is passed the migrationConfiguration property is set to the value
* of the configuration one (if any).
* Else a new configuration is created and assigned to the migrationConfiguration property.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return Configuration
*/
protected function getMigrationConfiguration(InputInterface $input, OutputInterface $output)
{
if (!$this->migrationConfiguration) {
$configHelper = new ConfigurationHelper($this->getConnection($input), $this->configuration);
$this->migrationConfiguration = $configHelper->getMigrationConfig($input, $this->getOutputWriter($output));
}
return $this->migrationConfiguration;
}
/**
* This method ensure that we stay compatible with symfony console 2.3 by using the deprecated dialog helper
* but use the ConfirmationQuestion when available.
*
* @param $question
* @param InputInterface $input
* @param OutputInterface $output
* @return mixed
*/
protected function askConfirmation($question, InputInterface $input, OutputInterface $output)
{
if ($this->getHelperSet()->has('question')) {
return $this->getHelper('question')->ask($input, $output, new ConfirmationQuestion($question));
} else {
return $this->getHelper('dialog')->askConfirmation($output, '<question>' . $question . '</question>', false);
}
}
/**
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
* @return \Doctrine\DBAL\Migrations\OutputWriter
*/
private function getOutputWriter(OutputInterface $output)
{
if (!$this->outputWriter) {
$this->outputWriter = new OutputWriter(function ($message) use ($output) {
return $output->writeln($message);
});
}
return $this->outputWriter;
}
/**
* @param \Symfony\Component\Console\Input\InputInterface $input
*
* @return \Doctrine\DBAL\Connection
* @throws \Doctrine\DBAL\DBALException
*/
private function getConnection(InputInterface $input)
{
if (!$this->connection) {
if ($input->getOption('db-configuration')) {
if (!file_exists($input->getOption('db-configuration'))) {
throw new \InvalidArgumentException("The specified connection file is not a valid file.");
}
$params = include $input->getOption('db-configuration');
if (!is_array($params)) {
throw new \InvalidArgumentException('The connection file has to return an array with database configuration parameters.');
}
$this->connection = \Doctrine\DBAL\DriverManager::getConnection($params);
} elseif (file_exists('migrations-db.php')) {
$params = include 'migrations-db.php';
if (!is_array($params)) {
throw new \InvalidArgumentException('The connection file has to return an array with database configuration parameters.');
}
$this->connection = \Doctrine\DBAL\DriverManager::getConnection($params);
} elseif ($this->getHelperSet()->has('connection')) {
$this->connection = $this->getHelper('connection')->getConnection();
} elseif ($this->configuration) {
$this->connection = $this->configuration->getConnection();
} else {
throw new \InvalidArgumentException('You have to specify a --db-configuration file or pass a Database Connection as a dependency to the Migrations.');
}
}
return $this->connection;
}
}

@ -0,0 +1,167 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Tools\Console\Command;
use Doctrine\DBAL\Migrations\Configuration\Configuration;
use Doctrine\DBAL\Version as DbalVersion;
use Doctrine\DBAL\Migrations\Provider\SchemaProviderInterface;
use Doctrine\DBAL\Migrations\Provider\OrmSchemaProvider;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
/**
* Command for generate migration classes by comparing your current database schema
* to your mapping information.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan Wage <jonwage@gmail.com>
*/
class DiffCommand extends GenerateCommand
{
/**
* @var SchemaProviderInterface
*/
protected $schemaProvider;
public function __construct(SchemaProviderInterface $schemaProvider=null)
{
$this->schemaProvider = $schemaProvider;
parent::__construct();
}
protected function configure()
{
parent::configure();
$this
->setName('migrations:diff')
->setDescription('Generate a migration by comparing your current database to your mapping information.')
->setHelp(<<<EOT
The <info>%command.name%</info> command generates a migration by comparing your current database to your mapping information:
<info>%command.full_name%</info>
You can optionally specify a <comment>--editor-cmd</comment> option to open the generated file in your favorite editor:
<info>%command.full_name% --editor-cmd=mate</info>
EOT
)
->addOption('filter-expression', null, InputOption::VALUE_OPTIONAL, 'Tables which are filtered by Regular Expression.');
}
public function execute(InputInterface $input, OutputInterface $output)
{
$isDbalOld = (DbalVersion::compare('2.2.0') > 0);
$configuration = $this->getMigrationConfiguration($input, $output);
$conn = $configuration->getConnection();
$platform = $conn->getDatabasePlatform();
if ($filterExpr = $input->getOption('filter-expression')) {
if ($isDbalOld) {
throw new \InvalidArgumentException('The "--filter-expression" option can only be used as of Doctrine DBAL 2.2');
}
$conn->getConfiguration()
->setFilterSchemaAssetsExpression($filterExpr);
}
$fromSchema = $conn->getSchemaManager()->createSchema();
$toSchema = $this->getSchemaProvider()->createSchema();
//Not using value from options, because filters can be set from config.yml
if ( ! $isDbalOld && $filterExpr = $conn->getConfiguration()->getFilterSchemaAssetsExpression()) {
foreach ($toSchema->getTables() as $table) {
$tableName = $table->getName();
if ( ! preg_match($filterExpr, $this->resolveTableName($tableName))) {
$toSchema->dropTable($tableName);
}
}
}
$up = $this->buildCodeFromSql($configuration, $fromSchema->getMigrateToSql($toSchema, $platform));
$down = $this->buildCodeFromSql($configuration, $fromSchema->getMigrateFromSql($toSchema, $platform));
if (! $up && ! $down) {
$output->writeln('No changes detected in your mapping information.', 'ERROR');
return;
}
$version = date('YmdHis');
$path = $this->generateMigration($configuration, $input, $version, $up, $down);
$output->writeln(sprintf('Generated new migration class to "<info>%s</info>" from schema differences.', $path));
}
private function buildCodeFromSql(Configuration $configuration, array $sql)
{
$currentPlatform = $configuration->getConnection()->getDatabasePlatform()->getName();
$code = array();
foreach ($sql as $query) {
if (stripos($query, $configuration->getMigrationsTableName()) !== false) {
continue;
}
$code[] = sprintf("\$this->addSql(%s);", var_export($query, true));
}
if (!empty($code)) {
array_unshift(
$code,
sprintf(
"\$this->abortIf(\$this->connection->getDatabasePlatform()->getName() != %s, %s);",
var_export($currentPlatform, true),
var_export(sprintf("Migration can only be executed safely on '%s'.", $currentPlatform), true)
),
""
);
}
return implode("\n", $code);
}
private function getSchemaProvider()
{
if (!$this->schemaProvider) {
$this->schemaProvider = new OrmSchemaProvider($this->getHelper('entityManager')->getEntityManager());
}
return $this->schemaProvider;
}
/**
* Resolve a table name from its fully qualified name. The `$name` argument
* comes from Doctrine\DBAL\Schema\Table#getName which can sometimes return
* a namespaced name with the form `{namespace}.{tableName}`. This extracts
* the table name from that.
*
* @param string $name
* @return string
*/
private function resolveTableName($name)
{
$pos = strpos($name, '.');
return false === $pos ? $name : substr($name, $pos + 1);
}
}

@ -0,0 +1,102 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Tools\Console\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
/**
* Command for executing single migrations up or down manually.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan Wage <jonwage@gmail.com>
*/
class ExecuteCommand extends AbstractCommand
{
protected function configure()
{
$this
->setName('migrations:execute')
->setDescription('Execute a single migration version up or down manually.')
->addArgument('version', InputArgument::REQUIRED, 'The version to execute.', null)
->addOption('write-sql', null, InputOption::VALUE_NONE, 'The path to output the migration SQL file instead of executing it.')
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute the migration as a dry run.')
->addOption('up', null, InputOption::VALUE_NONE, 'Execute the migration up.')
->addOption('down', null, InputOption::VALUE_NONE, 'Execute the migration down.')
->addOption('query-time', null, InputOption::VALUE_NONE, 'Time all the queries individually.')
->setHelp(<<<EOT
The <info>%command.name%</info> command executes a single migration version up or down manually:
<info>%command.full_name% YYYYMMDDHHMMSS</info>
If no <comment>--up</comment> or <comment>--down</comment> option is specified it defaults to up:
<info>%command.full_name% YYYYMMDDHHMMSS --down</info>
You can also execute the migration as a <comment>--dry-run</comment>:
<info>%command.full_name% YYYYMMDDHHMMSS --dry-run</info>
You can output the would be executed SQL statements to a file with <comment>--write-sql</comment>:
<info>%command.full_name% YYYYMMDDHHMMSS --write-sql</info>
Or you can also execute the migration without a warning message which you need to interact with:
<info>%command.full_name% --no-interaction</info>
EOT
);
parent::configure();
}
public function execute(InputInterface $input, OutputInterface $output)
{
$version = $input->getArgument('version');
$direction = $input->getOption('down') ? 'down' : 'up';
$configuration = $this->getMigrationConfiguration($input, $output);
$version = $configuration->getVersion($version);
$timeAllqueries = $input->getOption('query-time');
if ($path = $input->getOption('write-sql')) {
$path = is_bool($path) ? getcwd() : $path;
$version->writeSqlFile($path, $direction);
} else {
if ($input->isInteractive()) {
$question = 'WARNING! You are about to execute a database migration that could result in schema changes and data lost. Are you sure you wish to continue? (y/n)';
$execute = $this->askConfirmation($question, $input, $output);
} else {
$execute = true;
}
if ($execute) {
$version->execute($direction, (boolean) $input->getOption('dry-run'), $timeAllqueries);
} else {
$output->writeln('<error>Migration cancelled!</error>');
}
}
}
}

@ -0,0 +1,135 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Tools\Console\Command;
use Doctrine\DBAL\Migrations\Configuration\Configuration;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
/**
* Command for generating new blank migration classes
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan Wage <jonwage@gmail.com>
*/
class GenerateCommand extends AbstractCommand
{
private static $_template =
'<?php
namespace <namespace>;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version<version> extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
<up>
}
/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
<down>
}
}
';
protected function configure()
{
$this
->setName('migrations:generate')
->setDescription('Generate a blank migration class.')
->addOption('editor-cmd', null, InputOption::VALUE_OPTIONAL, 'Open file with this command upon creation.')
->setHelp(<<<EOT
The <info>%command.name%</info> command generates a blank migration class:
<info>%command.full_name%</info>
You can optionally specify a <comment>--editor-cmd</comment> option to open the generated file in your favorite editor:
<info>%command.full_name% --editor-cmd=mate</info>
EOT
);
parent::configure();
}
public function execute(InputInterface $input, OutputInterface $output)
{
$configuration = $this->getMigrationConfiguration($input, $output);
$version = date('YmdHis');
$path = $this->generateMigration($configuration, $input, $version);
$output->writeln(sprintf('Generated new migration class to "<info>%s</info>"', $path));
}
protected function generateMigration(Configuration $configuration, InputInterface $input, $version, $up = null, $down = null)
{
$placeHolders = array(
'<namespace>',
'<version>',
'<up>',
'<down>'
);
$replacements = array(
$configuration->getMigrationsNamespace(),
$version,
$up ? " " . implode("\n ", explode("\n", $up)) : null,
$down ? " " . implode("\n ", explode("\n", $down)) : null
);
$code = str_replace($placeHolders, $replacements, self::$_template);
$code = preg_replace('/^ +$/m', '', $code);
$dir = $configuration->getMigrationsDirectory();
$dir = $dir ? $dir : getcwd();
$dir = rtrim($dir, '/');
$path = $dir . '/Version' . $version . '.php';
if ( ! file_exists($dir)) {
throw new \InvalidArgumentException(sprintf('Migrations directory "%s" does not exist.', $dir));
}
file_put_contents($path, $code);
if ($editorCmd = $input->getOption('editor-cmd')) {
proc_open($editorCmd . ' ' . escapeshellarg($path), array(), $pipes);
}
return $path;
}
}

@ -0,0 +1,48 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Tools\Console\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Outputs the latest version number.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class LatestCommand extends AbstractCommand
{
protected function configure()
{
$this
->setName('migrations:latest')
->setDescription('Outputs the latest version number')
;
parent::configure();
}
public function execute(InputInterface $input, OutputInterface $output)
{
$configuration = $this->getMigrationConfiguration($input, $output);
$output->writeln($configuration->getLatestVersion());
}
}

@ -0,0 +1,192 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Tools\Console\Command;
use Doctrine\DBAL\Migrations\Configuration\Configuration;
use Doctrine\DBAL\Migrations\Migration;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
/**
* Command for executing a migration to a specified version or the latest available version.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan Wage <jonwage@gmail.com>
*/
class MigrateCommand extends AbstractCommand
{
protected function configure()
{
$this
->setName('migrations:migrate')
->setDescription('Execute a migration to a specified version or the latest available version.')
->addArgument('version', InputArgument::OPTIONAL, 'The version number (YYYYMMDDHHMMSS) or alias (first, prev, next, latest) to migrate to.', 'latest')
->addOption('write-sql', null, InputOption::VALUE_NONE, 'The path to output the migration SQL file instead of executing it.')
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute the migration as a dry run.')
->addOption('query-time', null, InputOption::VALUE_NONE, 'Time all the queries individually.')
->setHelp(<<<EOT
The <info>%command.name%</info> command executes a migration to a specified version or the latest available version:
<info>%command.full_name%</info>
You can optionally manually specify the version you wish to migrate to:
<info>%command.full_name% YYYYMMDDHHMMSS</info>
You can specify the version you wish to migrate to using an alias:
<info>%command.full_name% prev</info>
You can also execute the migration as a <comment>--dry-run</comment>:
<info>%command.full_name% YYYYMMDDHHMMSS --dry-run</info>
You can output the would be executed SQL statements to a file with <comment>--write-sql</comment>:
<info>%command.full_name% YYYYMMDDHHMMSS --write-sql</info>
Or you can also execute the migration without a warning message which you need to interact with:
<info>%command.full_name% --no-interaction</info>
You can also time all the different queries if you wanna know which one is taking so long:
<info>%command.full_name% --query-time</info>
EOT
);
parent::configure();
}
public function execute(InputInterface $input, OutputInterface $output)
{
$configuration = $this->getMigrationConfiguration($input, $output);
$migration = new Migration($configuration);
$this->outputHeader($configuration, $output);
$timeAllqueries = $input->getOption('query-time');
$executedMigrations = $configuration->getMigratedVersions();
$availableMigrations = $configuration->getAvailableVersions();
$executedUnavailableMigrations = array_diff($executedMigrations, $availableMigrations);
$version = $this->getVersionNameFromAlias($input->getArgument('version'), $output, $configuration);
if ($version === false) {
return 1;
}
if (!empty($executedUnavailableMigrations)) {
$output->writeln(sprintf(
'<error>WARNING! You have %s previously executed migrations'
. ' in the database that are not registered migrations.</error>',
count($executedUnavailableMigrations)
));
foreach ($executedUnavailableMigrations as $executedUnavailableMigration) {
$output->writeln(sprintf(
' <comment>>></comment> %s (<comment>%s</comment>)',
$configuration->getDateTime($executedUnavailableMigration),
$executedUnavailableMigration
));
}
$question = 'Are you sure you wish to continue? (y/n)';
if (! $this->canExecute($question, $input, $output)) {
$output->writeln('<error>Migration cancelled!</error>');
return 1;
}
}
if ($path = $input->getOption('write-sql')) {
$path = is_bool($path) ? getcwd() : $path;
$migration->writeSqlFile($path, $version);
} else {
$dryRun = (boolean) $input->getOption('dry-run');
// warn the user if no dry run and interaction is on
if (! $dryRun) {
$question = 'WARNING! You are about to execute a database migration'
. ' that could result in schema changes and data lost.'
. ' Are you sure you wish to continue? (y/n)';
if (! $this->canExecute($question, $input, $output)) {
$output->writeln('<error>Migration cancelled!</error>');
return 1;
}
}
$sql = $migration->migrate($version, $dryRun, $timeAllqueries);
if (empty($sql)) {
$output->writeln('<comment>No migrations to execute.</comment>');
}
}
}
/**
* @param string $question
* @param InputInterface $input
* @param OutputInterface $output
* @return bool
*/
private function canExecute($question, InputInterface $input, OutputInterface $output)
{
if ($input->isInteractive() && ! $this->askConfirmation($question, $input, $output)) {
return false;
}
return true;
}
/**
* @param string $versionAlias
* @param OutputInterface $output
* @param Configuration $configuration
* @return bool|string
*/
private function getVersionNameFromAlias($versionAlias, OutputInterface $output, Configuration $configuration)
{
$version = $configuration->resolveVersionAlias($versionAlias);
if ($version === null) {
if ($versionAlias == 'prev') {
$output->writeln('<error>Already at first version.</error>');
return false;
}
if ($versionAlias == 'next') {
$output->writeln('<error>Already at latest version.</error>');
return false;
}
$output->writeln(sprintf(
'<error>Unknown version: %s</error>',
$output->getFormatter()->escape($versionAlias)
));
return false;
}
return $version;
}
}

@ -0,0 +1,140 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Tools\Console\Command;
use Doctrine\DBAL\Migrations\Configuration\Configuration;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
/**
* Command to view the status of a set of migrations.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan Wage <jonwage@gmail.com>
*/
class StatusCommand extends AbstractCommand
{
protected function configure()
{
$this
->setName('migrations:status')
->setDescription('View the status of a set of migrations.')
->addOption('show-versions', null, InputOption::VALUE_NONE, 'This will display a list of all available migrations and their status')
->setHelp(<<<EOT
The <info>%command.name%</info> command outputs the status of a set of migrations:
<info>%command.full_name%</info>
You can output a list of all available migrations and their status with <comment>--show-versions</comment>:
<info>%command.full_name% --show-versions</info>
EOT
);
parent::configure();
}
public function execute(InputInterface $input, OutputInterface $output)
{
$configuration = $this->getMigrationConfiguration($input, $output);
$formattedVersions = array();
foreach (array('prev', 'current', 'next', 'latest') as $alias) {
$version = $configuration->resolveVersionAlias($alias);
if ($version === null) {
if ($alias == 'next') {
$formattedVersions[$alias] = 'Already at latest version';
} elseif ($alias == 'prev') {
$formattedVersions[$alias] = 'Already at first version';
}
} elseif ($version === '0') {
$formattedVersions[$alias] = '<comment>0</comment>';
} else {
$formattedVersions[$alias] = $configuration->getDateTime($version) . ' (<comment>' . $version . '</comment>)';
}
}
$executedMigrations = $configuration->getMigratedVersions();
$availableMigrations = $configuration->getAvailableVersions();
$executedUnavailableMigrations = array_diff($executedMigrations, $availableMigrations);
$numExecutedUnavailableMigrations = count($executedUnavailableMigrations);
$newMigrations = count(array_diff($availableMigrations, $executedMigrations));
$output->writeln("\n <info>==</info> Configuration\n");
$info = array(
'Name' => $configuration->getName() ? $configuration->getName() : 'Doctrine Database Migrations',
'Database Driver' => $configuration->getConnection()->getDriver()->getName(),
'Database Name' => $configuration->getConnection()->getDatabase(),
'Configuration Source' => $configuration instanceof \Doctrine\DBAL\Migrations\Configuration\AbstractFileConfiguration ? $configuration->getFile() : 'manually configured',
'Version Table Name' => $configuration->getMigrationsTableName(),
'Migrations Namespace' => $configuration->getMigrationsNamespace(),
'Migrations Directory' => $configuration->getMigrationsDirectory(),
'Previous Version' => $formattedVersions['prev'],
'Current Version' => $formattedVersions['current'],
'Next Version' => $formattedVersions['next'],
'Latest Version' => $formattedVersions['latest'],
'Executed Migrations' => count($executedMigrations),
'Executed Unavailable Migrations' => $numExecutedUnavailableMigrations > 0 ? '<error>'.$numExecutedUnavailableMigrations.'</error>' : 0,
'Available Migrations' => count($availableMigrations),
'New Migrations' => $newMigrations > 0 ? '<question>' . $newMigrations . '</question>' : 0
);
foreach ($info as $name => $value) {
$output->writeln(' <comment>>></comment> ' . $name . ': ' . str_repeat(' ', 50 - strlen($name)) . $value);
}
if ($input->getOption('show-versions')) {
if ($migrations = $configuration->getMigrations()) {
$executedUnavailableMigrations = $migrations;
$output->writeln("\n <info>==</info> Available Migration Versions\n");
$this->showVersions($migrations, $configuration, $output);
}
if (!empty($executedUnavailableMigrations)) {
$output->writeln("\n <info>==</info> Previously Executed Unavailable Migration Versions\n");
$this->showVersions($executedUnavailableMigrations, $configuration, $output);
}
}
}
private function showVersions($migrations, Configuration $configuration, $output)
{
$migratedVersions = $configuration->getMigratedVersions();
foreach($migrations as $version) {
$isMigrated = in_array($version->getVersion(), $migratedVersions);
$status = $isMigrated ? '<info>migrated</info>' : '<error>not migrated</error>';
$migrationDescription = '';
if ($version->getMigration()->getDescription()) {
$migrationDescription = str_repeat(' ', 5) . $version->getMigration()->getDescription();
}
$formattedVersion = $configuration->getDateTime($version->getVersion());
$output->writeln(' <comment>>></comment> ' . $formattedVersion .
' (<comment>' . $version->getVersion() . '</comment>)' .
str_repeat(' ', 49 - strlen($formattedVersion) - strlen($version->getVersion())) .
$status . $migrationDescription);
}
}
}

@ -0,0 +1,177 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Tools\Console\Command;
use Doctrine\DBAL\Migrations\MigrationException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
/**
* Command for manually adding and deleting migration versions from the version table.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan Wage <jonwage@gmail.com>
*/
class VersionCommand extends AbstractCommand
{
/**
* The Migrations Configuration instance
*
* @var \Doctrine\DBAL\Migrations\Configuration\Configuration
*/
private $configuration;
/**
* Whether or not the versions have to be marked as migrated or not
*
* @var boolean
*/
private $markMigrated;
protected function configure()
{
$this
->setName('migrations:version')
->setDescription('Manually add and delete migration versions from the version table.')
->addArgument('version', InputArgument::OPTIONAL, 'The version to add or delete.', null)
->addOption('add', null, InputOption::VALUE_NONE, 'Add the specified version.')
->addOption('delete', null, InputOption::VALUE_NONE, 'Delete the specified version.')
->addOption('all', null, InputOption::VALUE_NONE, 'Apply to all the versions.')
->addOption('range-from', null, InputOption::VALUE_OPTIONAL, 'Apply from specified version.')
->addOption('range-to', null, InputOption::VALUE_OPTIONAL, 'Apply to specified version.')
->setHelp(<<<EOT
The <info>%command.name%</info> command allows you to manually add, delete or synchronize migration versions from the version table:
<info>%command.full_name% YYYYMMDDHHMMSS --add</info>
If you want to delete a version you can use the <comment>--delete</comment> option:
<info>%command.full_name% YYYYMMDDHHMMSS --delete</info>
If you want to synchronize by adding or deleting all migration versions available in the version table you can use the <comment>--all</comment> option:
<info>%command.full_name% --add --all</info>
<info>%command.full_name% --delete --all</info>
If you want to synchronize by adding or deleting some range of migration versions available in the version table you can use the <comment>--range-from/--range-to</comment> option:
<info>%command.full_name% --add --range-from=YYYYMMDDHHMMSS --range-to=YYYYMMDDHHMMSS</info>
<info>%command.full_name% --delete --range-from=YYYYMMDDHHMMSS --range-to=YYYYMMDDHHMMSS</info>
You can also execute this command without a warning message which you need to interact with:
<info>%command.full_name% --no-interaction</info>
EOT
);
parent::configure();
}
public function execute(InputInterface $input, OutputInterface $output)
{
$this->configuration = $this->getMigrationConfiguration($input, $output);
if ( ! $input->getOption('add') && ! $input->getOption('delete')) {
throw new \InvalidArgumentException('You must specify whether you want to --add or --delete the specified version.');
}
$this->markMigrated = (boolean) $input->getOption('add');
if ($input->isInteractive()) {
$question = 'WARNING! You are about to add, delete or synchronize migration versions from the version table that could result in data lost. Are you sure you wish to continue? (y/n)';
$confirmation = $this->askConfirmation($question, $input, $output);
if ($confirmation) {
$this->markVersions($input);
} else {
$output->writeln('<error>Migration cancelled!</error>');
}
} else {
$this->markVersions($input);
}
}
private function markVersions(InputInterface $input)
{
$affectedVersion = $input->getArgument('version');
$allOption = $input->getOption('all');
$rangeFromOption = $input->getOption('range-from');
$rangeToOption = $input->getOption('range-to');
if ($allOption && ($rangeFromOption !== null || $rangeToOption !== null)) {
throw new \InvalidArgumentException('Options --all and --range-to/--range-from both used. You should use only one of them.');
} elseif ($rangeFromOption !== null ^ $rangeToOption !== null) {
throw new \InvalidArgumentException('Options --range-to and --range-from should be used together.');
}
if ($allOption === true) {
$availableVersions = $this->configuration->getAvailableVersions();
foreach ($availableVersions as $version) {
$this->mark($version, true);
}
} elseif ($rangeFromOption !== null && $rangeToOption !== null) {
$availableVersions = $this->configuration->getAvailableVersions();
foreach ($availableVersions as $version) {
if ($version >= $rangeFromOption && $version <= $rangeToOption) {
$this->mark($version, true);
}
}
} else {
$this->mark($affectedVersion);
}
}
private function mark($version, $all = false)
{
if ( ! $this->configuration->hasVersion($version)) {
throw MigrationException::unknownMigrationVersion($version);
}
$version = $this->configuration->getVersion($version);
if ($this->markMigrated && $this->configuration->hasVersionMigrated($version)) {
$marked = true;
if (! $all) {
throw new \InvalidArgumentException(sprintf('The version "%s" already exists in the version table.', $version));
}
}
if ( ! $this->markMigrated && ! $this->configuration->hasVersionMigrated($version)) {
$marked = false;
if (! $all) {
throw new \InvalidArgumentException(sprintf('The version "%s" does not exists in the version table.', $version));
}
}
if ( ! isset($marked)) {
if ($this->markMigrated) {
$version->markMigrated();
} else {
$version->markNotMigrated();
}
}
}
}

@ -0,0 +1,107 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations\Tools\Console\Helper;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Migrations\Configuration\Configuration;
use Doctrine\DBAL\Migrations\OutputWriter;
use Symfony\Component\Console\Input\InputInterface;
/**
* Class ConfigurationHelper
* @package Doctrine\DBAL\Migrations\Tools\Console\Helper
* @internal
*/
final class ConfigurationHelper
{
/**
* @var Connection
*/
private $connection;
/**
* @var Configuration
*/
private $configuration;
public function __construct(Connection $connection = null, Configuration $configuration = null)
{
$this->connection = $connection;
$this->configuration = $configuration;
}
public function getMigrationConfig(InputInterface $input, OutputWriter $outputWriter)
{
/**
* If a configuration option is passed to the command line, use that configuration
* instead of any other one.
*/
if ($input->getOption('configuration')) {
$outputWriter->write("Loading configuration from command option: " . $input->getOption('configuration'));
return $this->loadConfig($input->getOption('configuration'), $outputWriter);
}
/**
* If a configuration has already been set using DI or a Setter use it.
*/
if ($this->configuration) {
$outputWriter->write("Loading configuration from the integration code of your framework (setter).");
return $this->configuration;
}
/**
* If no any other config has been found, look for default config file in the path.
*/
$defaultConfig = array(
'migrations.xml',
'migrations.yml',
'migrations.yaml',
);
foreach ($defaultConfig as $config) {
if ($this->configExists($config)) {
$outputWriter->write("Loading configuration from file: $config");
return $this->loadConfig($config, $outputWriter);
}
}
return new Configuration($this->connection, $outputWriter);
}
private function configExists($config)
{
return file_exists($config);
}
private function loadConfig($config, OutputWriter $outputWriter)
{
$info = pathinfo($config);
$class = 'Doctrine\DBAL\Migrations\Configuration';
$class .= $info['extension'] === 'xml' ? '\XmlConfiguration' : '\YamlConfiguration';
$configuration = new $class($this->connection, $outputWriter);
$configuration->load($config);
return $configuration;
}
}

@ -0,0 +1,392 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Migrations;
use Doctrine\DBAL\Migrations\Configuration\Configuration;
/**
* Class which wraps a migration version and allows execution of the
* individual migration version up or down method.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class Version
{
const STATE_NONE = 0;
const STATE_PRE = 1;
const STATE_EXEC = 2;
const STATE_POST = 3;
/**
* The Migrations Configuration instance for this migration
*
* @var Configuration
*/
private $configuration;
/**
* The OutputWriter object instance used for outputting information
*
* @var OutputWriter
*/
private $outputWriter;
/**
* The version in timestamp format (YYYYMMDDHHMMSS)
*
* @param int
*/
private $version;
/**
* @var \Doctrine\DBAL\Schema\AbstractSchemaManager
*/
private $sm;
/**
* @var \Doctrine\DBAL\Platforms\AbstractPlatform
*/
private $platform;
/**
* The migration instance for this version
*
* @var AbstractMigration
*/
private $migration;
/**
* @var \Doctrine\DBAL\Connection
*/
private $connection;
/**
* @var string
*/
private $class;
/** The array of collected SQL statements for this version */
private $sql = array();
/** The array of collected parameters for SQL statements for this version */
private $params = array();
/** The array of collected types for SQL statements for this version */
private $types = array();
/** The time in seconds that this migration version took to execute */
private $time;
/**
* @var int
*/
private $state = self::STATE_NONE;
public function __construct(Configuration $configuration, $version, $class)
{
$this->configuration = $configuration;
$this->outputWriter = $configuration->getOutputWriter();
$this->class = $class;
$this->connection = $configuration->getConnection();
$this->sm = $this->connection->getSchemaManager();
$this->platform = $this->connection->getDatabasePlatform();
$this->migration = new $class($this);
$this->version = $version;
}
/**
* Returns the string version in the format YYYYMMDDHHMMSS
*
* @return string $version
*/
public function getVersion()
{
return $this->version;
}
/**
* Returns the Migrations Configuration object instance
*
* @return Configuration $configuration
*/
public function getConfiguration()
{
return $this->configuration;
}
/**
* Check if this version has been migrated or not.
*
* @return boolean
*/
public function isMigrated()
{
return $this->configuration->hasVersionMigrated($this);
}
public function markMigrated()
{
$this->configuration->createMigrationTable();
$this->connection->insert(
$this->configuration->getMigrationsTableName(),
array('version' => $this->version)
);
}
public function markNotMigrated()
{
$this->configuration->createMigrationTable();
$this->connection->delete(
$this->configuration->getMigrationsTableName(),
array('version' => $this->version)
);
}
/**
* Add some SQL queries to this versions migration
*
* @param array|string $sql
* @param array $params
* @param array $types
*/
public function addSql($sql, array $params = array(), array $types = array())
{
if (is_array($sql)) {
foreach ($sql as $key => $query) {
$this->sql[] = $query;
if (isset($params[$key])) {
$this->params[count($this->sql) - 1] = $params[$key];
$this->types[count($this->sql) - 1] = isset($types[$key]) ? $types[$key] : array();
}
}
} else {
$this->sql[] = $sql;
if (!empty($params)) {
$index = count($this->sql) - 1;
$this->params[$index] = $params;
$this->types[$index] = $types;
}
}
}
/**
* Write a migration SQL file to the given path
*
* @param string $path The path to write the migration SQL file.
* @param string $direction The direction to execute.
*
* @return boolean $written
*/
public function writeSqlFile($path, $direction = 'up')
{
$queries = $this->execute($direction, true);
$this->outputWriter->write("\n# Version " . $this->version . "\n");
$sqlQueries = [$this->version => $queries];
$sqlWriter = new SqlFileWriter(
$this->configuration->getMigrationsTableName(),
$path,
$this->outputWriter
);
return $sqlWriter->write($sqlQueries, $direction);
}
/**
* @return AbstractMigration
*/
public function getMigration()
{
return $this->migration;
}
/**
* Execute this migration version up or down and and return the SQL.
*
* @param string $direction The direction to execute the migration.
* @param boolean $dryRun Whether to not actually execute the migration SQL and just do a dry run.
* @param boolean $timeAllQueries Measuring or not the execution time of each SQL query.
*
* @return array $sql
*
* @throws \Exception when migration fails
*/
public function execute($direction, $dryRun = false, $timeAllQueries = false)
{
$this->sql = array();
$transaction = $this->migration->isTransactional();
if ($transaction) {
//only start transaction if in transactional mode
$this->connection->beginTransaction();
}
try {
$migrationStart = microtime(true);
$this->state = self::STATE_PRE;
$fromSchema = $this->sm->createSchema();
$this->migration->{'pre' . ucfirst($direction)}($fromSchema);
if ($direction === 'up') {
$this->outputWriter->write("\n" . sprintf(' <info>++</info> migrating <comment>%s</comment>', $this->version) . "\n");
} else {
$this->outputWriter->write("\n" . sprintf(' <info>--</info> reverting <comment>%s</comment>', $this->version) . "\n");
}
$this->state = self::STATE_EXEC;
$toSchema = clone $fromSchema;
$this->migration->$direction($toSchema);
$this->addSql($fromSchema->getMigrateToSql($toSchema, $this->platform));
if (! $dryRun) {
if (!empty($this->sql)) {
foreach ($this->sql as $key => $query) {
$queryStart = microtime(true);
if ( ! isset($this->params[$key])) {
$this->outputWriter->write(' <comment>-></comment> ' . $query);
$this->connection->exec($query);
} else {
$this->outputWriter->write(sprintf(' <comment>-</comment> %s (with parameters)', $query));
$this->connection->executeQuery($query, $this->params[$key], $this->types[$key]);
}
$this->outputQueryTime($queryStart, $timeAllQueries);
}
} else {
$this->outputWriter->write(sprintf(
'<error>Migration %s was executed but did not result in any SQL statements.</error>',
$this->version
));
}
if ($direction === 'up') {
$this->markMigrated();
} else {
$this->markNotMigrated();
}
} else {
foreach ($this->sql as $query) {
$this->outputWriter->write(' <comment>-></comment> ' . $query);
}
}
$this->state = self::STATE_POST;
$this->migration->{'post' . ucfirst($direction)}($toSchema);
$migrationEnd = microtime(true);
$this->time = round($migrationEnd - $migrationStart, 2);
if ($direction === 'up') {
$this->outputWriter->write(sprintf("\n <info>++</info> migrated (%ss)", $this->time));
} else {
$this->outputWriter->write(sprintf("\n <info>--</info> reverted (%ss)", $this->time));
}
if ($transaction) {
//commit only if running in transactional mode
$this->connection->commit();
}
$this->state = self::STATE_NONE;
return $this->sql;
} catch (SkipMigrationException $e) {
if ($transaction) {
//only rollback transaction if in transactional mode
$this->connection->rollback();
}
if ($dryRun === false) {
// now mark it as migrated
if ($direction === 'up') {
$this->markMigrated();
} else {
$this->markNotMigrated();
}
}
$this->outputWriter->write(sprintf("\n <info>SS</info> skipped (Reason: %s)", $e->getMessage()));
$this->state = self::STATE_NONE;
return array();
} catch (\Exception $e) {
$this->outputWriter->write(sprintf(
'<error>Migration %s failed during %s. Error %s</error>',
$this->version, $this->getExecutionState(), $e->getMessage()
));
if ($transaction) {
//only rollback transaction if in transactional mode
$this->connection->rollback();
}
$this->state = self::STATE_NONE;
throw $e;
}
}
public function getExecutionState()
{
switch ($this->state) {
case self::STATE_PRE:
return 'Pre-Checks';
case self::STATE_POST:
return 'Post-Checks';
case self::STATE_EXEC:
return 'Execution';
default:
return 'No State';
}
}
private function outputQueryTime($queryStart, $timeAllQueries = false)
{
if ($timeAllQueries !== false) {
$queryEnd = microtime(true);
$queryTime = round($queryEnd - $queryStart, 4);
$this->outputWriter->write(sprintf(" <info>%ss</info>", $queryTime));
}
}
/**
* Returns the time this migration version took to execute
*
* @return integer $time The time this migration version took to execute
*/
public function getTime()
{
return $this->time;
}
public function __toString()
{
return $this->version;
}
}

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" bootstrap="tests/bootstrap.php">
<testsuites>
<testsuite name="Doctrine2 Database Migrations Test Suite">
<directory>./tests/Doctrine/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./lib/</directory>
</whitelist>
</filter>
<listeners>
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" >
<arguments>
<array>
<element key="slowThreshold">
<integer>200</integer>
</element>
<element key="reportLength">
<integer>10</integer>
</element>
</array>
</arguments>
</listener>
</listeners>
<logging>
<log type="coverage-html" target="build/coverage" title="doctrine migrations"
charset="UTF-8" yui="true" highlight="true" lowUpperBound="5"
highLowerBound="70" />
<log type="coverage-clover" target="build/logs/clover.xml"/>
</logging>
</phpunit>
Loading…
Cancel
Save