Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial Release

  • Loading branch information...
commit 6b1cef83c78357d9012877b2ceb1e65ee1f062ea 0 parents
@davedevelopment authored
9 .gitmodules
@@ -0,0 +1,9 @@
+[submodule "vendor/Symfony/Component/Console"]
+ path = vendor/Symfony/Component/Console
+ url = https://github.com/symfony/Console.git
+[submodule "vendor/Symfony/Component/ClassLoader"]
+ path = vendor/Symfony/Component/ClassLoader
+ url = https://github.com/symfony/ClassLoader
+[submodule "vendor/Symfony/Component/Config"]
+ path = vendor/Symfony/Component/Config
+ url = https://github.com/symfony/Config.git
21 LICENCE
@@ -0,0 +1,21 @@
+Pimple: Copyright (c) 2009,2010,2011 Fabien Potencier
+
+Phpmig: Copyright (C) 2011 Dave Marshall <dave.marshall@atstsolutions.co.uk>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
163 README.md
@@ -0,0 +1,163 @@
+Phpmig
+======
+
+What is it?
+-----------
+
+Phpmig is a (database) migration tool for php, that should be adaptable for use with most PHP 5.3+ projects. It's kind of like [doctrine migrations][doctrinemigrations], without the [doctrine][doctrine]. Although you can use doctrine if you want. And ironically, I use doctrine in my examples.
+
+How does it work?
+-----------------
+
+ $ phpmig migrate
+
+Phpmig aims to be vendor/framework independant, and in doing so, requires you to do a little bit of work up front to use it.
+
+Phpmig requires a bootstrap file, that must return an object that implements the ArrayAccess interface with several predefined keys. We recommend returning an instance of [Pimple][pimple], a simple dependency injection container (there's a version bundled at \Phpmig\Pimple\Pimple). This is also an ideal opportunity to expose your own services to the migrations themselves, which have access to the container.
+
+Getting Started
+---------------
+
+The best way to install phpmig is using pear
+
+ $ sudo pear channel-discover pear.atstsolutions.co.uk
+ $ sudo pear install atst/phpmig-alpha
+
+Phpmig can do a little configuring for you to get started, go to the root of your project and:
+
+ $ phpmig init
+ +d ./migrations Place your migration files in here
+ +f ./phpmig.php Create services in here
+ $
+
+It can generate migrations, but you have to tell it where. Phpmig gets you to supply it with a list of migrations, so it doesn't know where to put them. Migration files should be named versionnumber_name.php, where version number is made up of 0-9 and name is CamelCase or snake\_case. Each migration file should contain a class with the same name as the file in CamelCase.
+
+ $ phpmig generate AddRatingToLolCats ./migrations
+ +f ./migrations/20111018171411_AddRatingToLolCats.php
+ $ phpmig status
+
+ Status Migration ID Migration Name
+ -----------------------------------------
+ down 20111018171929 AddRatingToLolCats
+
+
+Use the migrate command to run migrations
+
+ $ phpmig migrate
+ == 20111018171411 AddRatingToLolCats migrating
+ == 20111018171411 AddRatingToLolCats migrated 0.0005s
+ $ phpmig status
+
+ Status Migration ID Migration Name
+ -----------------------------------------
+ up 20111018171929 AddRatingToLolCats
+
+ $
+
+Better Persistence
+------------------
+
+The init command creates a bootstrap file that specifies a flat file to use to
+track which migrations have been run, which isn't great. You can use the
+provided adapters to store this information in your database. For example, to
+use Doctrine's DBAL:
+
+``` php
+<?php
+
+# phpmig.php
+
+// do some autoloading of Doctrine here
+
+use \Phpmig\Adapter,
+ \Phpmig\Pimple\Pimple,
+ \Doctrine\DBAL\DriverManager;
+
+$container = new Pimple();
+
+$container['db'] = $container->share(function() {
+ return DriverManager::getConnection(array(
+ 'driver' => 'pdo_sqlite',
+ 'path' => __DIR__ . DIRECTORY_SEPARATOR . 'db.sqlite',
+ ));
+});
+
+$container['phpmig.adapter'] = $container->share(function() use ($container) {
+ return new Adapter\Doctrine\DBAL($container['db'], 'migrations');
+});
+
+$container['phpmig.migrations'] = function() {
+ return glob(__DIR__ . DIRECTORY_SEPARATOR . 'migrations/*.php');
+};
+
+return $container;
+```
+
+Writing Migrations
+------------------
+
+The migrations should extend the Phpmig\Migration\Migration class, and have
+access to the container. For example, assuming you've rewritten your bootstrap
+file like above:
+
+``` php
+<?php
+
+use Phpmig\Migration\Migration;
+
+class AddRatingToLolCats extends Migration
+{
+ /**
+ * Do the migration
+ */
+ public function up()
+ {
+ $sql = "ALTER TABLE `lol_cats` ADD COLUMN `rating` INT(10) UNSIGNED NULL";
+ $container = $this->getContainer();
+ $container['db']->query($sql);
+ }
+
+ /**
+ * Undo the migration
+ */
+ public function down()
+ {
+ $sql = "ALTER TABLE `lol_cats` DROP COLUMN `rating`";
+ $container = $this->getContainer();
+ $container['db']->query($sql);
+ }
+}
+```
+
+Todo
+----
+
+* Some sort of migration manager, that will take some of the logic out of the commands for calculating which migrations have been run, which need running etc
+* Adapters for Zend\_Db and/or Zend\_Db\_Table and others?
+* Redo and rollback commands
+* Tests!
+* Configuration?
+* Someway of protecting against class definition clashes with regard to the symfony dependencies and the user supplied bootstrap?
+
+Contributing
+------------
+
+Feel free to fork and send me pull requests, but I don't have a 1.0 release yet, so I may change the API quite frequently. If you want to implement somthing that I might easily break, please drop me an email
+
+Inspiration
+-----------
+
+I basically started copying [ActiveRecord::Migrations][activerecordmigrations] in terms of the migration features, the bootstrapping was my own idea, the layout of the code was inspired by [Symfony][symfony] and [Behat][behat]
+
+Copyright
+---------
+
+[Pimple][pimple] is copyright Fabien Potencier. Everything I haven't copied from anyone else is Copyright (c) 2011 Dave Marshall. See LICENCE for further details
+
+
+[pimple]:https://github.com/fabpot/Pimple
+[doctrinemigrations]:https://github.com/doctrine/migrations
+[doctrine]:https://github.com/doctrine
+[behat]:http://behat.org/
+[symfony]:http://symfony.com/
+[activerecordmigrations]:http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
22 bin/phpmig
@@ -0,0 +1,22 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+define('PHPMIG_VERSION', 'dev');
+
+if (is_dir(__DIR__ . '/../src/Phpmig')) {
+ require_once __DIR__ . "/../src/Phpmig/autoload.php.dist";
+} else {
+ require_once "Phpmig/autoload.php.dist";
+}
+
+$app = new Phpmig\Console\PhpmigApplication(PHPMIG_VERSION);
+$app->run();
51 bin/release
@@ -0,0 +1,51 @@
+#!/usr/bin/env php
+<?php
+
+if (!isset($argv[1])) {
+ throw new RuntimeException('You must provide version.');
+}
+$version = $argv[1];
+
+if (!isset($argv[2])) {
+ throw new RuntimeException('You must provide stability status (alpha/beta/stable).');
+}
+$stability = $argv[2];
+
+$package = __DIR__ . '/../package.xml';
+$oldPackageContents = file_get_contents($package);
+
+$replace = array(
+ '@@DATE@@' => date('Y-m-d'),
+ '@@TIME@@' => date('H:i:s'),
+ '@@VERSION@@' => $version,
+ '@@STABILITY@@' => $stability,
+);
+
+$contents = array();
+$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__ . '/../src/'));
+
+while($it->valid()) {
+
+ if (!$it->isDot() && !$it->isDir()) {
+ $name = str_replace(__DIR__ . '/../', '', $it->key());
+ $installAs = $it->getSubPathName();
+ $contents[] = "<file role=\"php\" install-as=\"$installAs\" name=\"$name\" />";
+ }
+
+ $it->next();
+}
+
+$replace['@@CONTENTS@@'] = implode("\n", $contents);
+
+$newPackageContents = $oldPackageContents;
+
+foreach($replace as $s => $r) {
+ $newPackageContents = str_replace($s, $r, $newPackageContents);
+}
+
+file_put_contents($package, $newPackageContents);
+
+system('pear package');
+
+file_put_contents($package, $oldPackageContents);
+exit(0);
90 package.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
+ <name>phpmig</name>
+ <channel>pear.atstsolutions.co.uk</channel>
+ <summary>A simple migrations tool for php.</summary>
+ <description>A simple migrations tool for php.</description>
+ <lead>
+ <name>Dave Marshall</name>
+ <user>davedevelopment</user>
+ <email>dave.marshall@atstsolutions.co.uk</email>
+ <active>yes</active>
+ </lead>
+ <date>@@DATE@@</date>
+ <time>@@TIME@@</time>
+ <version>
+ <release>@@VERSION@@</release>
+ <api>@@VERSION@@</api>
+ </version>
+ <stability>
+ <release>@@STABILITY@@</release>
+ <api>@@STABILITY@@</api>
+ </stability>
+ <license uri="https://github.com/davedevelopment/phpmig/blob/develop/LICENCE">MIT Licence</license>
+ <notes>
+-
+ </notes>
+ <contents>
+ <dir name="/">
+ @@CONTENTS@@
+ <file role="php" install-as="Phpmig/autoload.php.dist" name="src/Phpmig/autoload.php.dist"><tasks:replace from="@@PHP_DIR@@" to="php_dir" type="pear-config"/></file>
+ <file role="script" name="bin/phpmig"><tasks:replace from="/usr/bin/env php" to="php_bin" type="pear-config"/><tasks:replace from="dev" to="version" type="package-info"/></file>
+ <file role="doc" name="README.md"/>
+ <file role="doc" name="LICENCE"/>
+ </dir>
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.3.0</min>
+ </php>
+ <pearinstaller>
+ <min>1.6.0</min>
+ </pearinstaller>
+ <package>
+ <name>ClassLoader</name>
+ <channel>pear.symfony.com</channel>
+ <min>2.0.0</min>
+ <max>3.999.9999</max>
+ </package>
+ <package>
+ <name>Config</name>
+ <channel>pear.symfony.com</channel>
+ <min>2.0.0</min>
+ <max>3.999.9999</max>
+ </package>
+ <package>
+ <name>Console</name>
+ <channel>pear.symfony.com</channel>
+ <min>2.0.0</min>
+ <max>3.999.9999</max>
+ </package>
+ </required>
+ </dependencies>
+ <phprelease>
+ <filelist>
+ <install as="phpmig" name="bin/phpmig"/>
+ </filelist>
+ </phprelease>
+ <changelog>
+ <release>
+ <version>
+ <release>@@VERSION@@</release>
+ <api>@@VERSION@@</api>
+ </version>
+ <stability>
+ <release>@@STABILITY@@</release>
+ <api>@@STABILITY@@</api>
+ </stability>
+ <date>@@DATE@@</date>
+ <license>MIT Licence</license>
+ <notes>
+ </notes>
+ </release>
+ </changelog>
+ <phprelease>
+ <filelist>
+ <install as="phpmig" name="bin/phpmig"/>
+ </filelist>
+ </phprelease>
+</package>
65 src/Phpmig/Adapter/AdapterInterface.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Adapter
+ */
+namespace Phpmig\Adapter;
+
+use \Phpmig\Migration\Migration;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Adapter interface
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk
+ */
+interface AdapterInterface
+{
+ /**
+ * Get all migrated version numbers
+ *
+ * @return array
+ */
+ public function fetchAll();
+
+ /**
+ * Up
+ *
+ * @param Migration $migration
+ * @return AdapterInterface
+ */
+ public function up(Migration $migration);
+
+ /**
+ * Down
+ *
+ * @param Migration $migration
+ * @return AdapterInterface
+ */
+ public function down(Migration $migration);
+
+ /**
+ * Is the schema ready?
+ *
+ * @return bool
+ */
+ public function hasSchema();
+
+ /**
+ * Create Schema
+ *
+ * @return AdapterInterface
+ */
+ public function createSchema();
+}
+
+
+
130 src/Phpmig/Adapter/Doctrine/DBAL.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Adapter
+ */
+namespace Phpmig\Adapter\Doctrine;
+
+use \Doctrine\DBAL\Connection,
+ \Doctrine\DBAL\Schema\Schema,
+ \Phpmig\Migration\Migration,
+ \Phpmig\Adapter\AdapterInterface;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+
+/**
+ * Phpmig adapter for doctrine dbal connection
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk>
+ */
+class DBAL implements AdapterInterface
+{
+ /**
+ * @var \Doctrine\DBAL\Connection
+ */
+ protected $connection = null;
+
+ /**
+ * @var string
+ */
+ protected $tableName = null;
+
+ /**
+ * Constructor
+ *
+ * @param Connection $connection
+ * @param string $tableName
+ */
+ public function __construct(Connection $connection, $tableName)
+ {
+ $this->connection = $connection;
+ $this->tableName = $tableName;
+ }
+
+ /**
+ * Fetch all
+ *
+ * @return array
+ */
+ public function fetchAll()
+ {
+ $tableName = $this->connection->quoteIdentifier($this->tableName);
+ $sql = "SELECT `version` FROM $tableName ORDER BY `version` ASC";
+ $all = $this->connection->fetchAll($sql);
+ return array_map(function($v) {return $v['version'];}, $all);
+ }
+
+ /**
+ * Up
+ *
+ * @param Migration $migration
+ * @return DBAL
+ */
+ public function up(Migration $migration)
+ {
+ $this->connection->insert($this->tableName, array(
+ 'version' => $migration->getVersion(),
+ ));
+
+ return $this;
+ }
+
+ /**
+ * Down
+ *
+ * @param Migration $migration
+ * @return DBAL
+ */
+ public function down(Migration $migration)
+ {
+ $this->connection->delete($this->tableName, array(
+ 'version' => $migration->getVersion(),
+ ));
+
+ return $this;
+ }
+
+ /**
+ * Is the schema ready?
+ *
+ * @return bool
+ */
+ public function hasSchema()
+ {
+ $sm = $this->connection->getSchemaManager();
+ $tables = $sm->listTables();
+ foreach($tables as $table) {
+ if ($table->getName() == $this->tableName) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Create Schema
+ *
+ * @return DBAL
+ */
+ public function createSchema()
+ {
+ $schema = new \Doctrine\DBAL\Schema\Schema();
+ $table = $schema->createTable($this->tableName);
+ $table->addColumn("version", "integer", array("unsigned" => true));
+ $queries = $schema->toSql($this->connection->getDatabasePlatform());
+ foreach($queries as $sql) {
+ $this->connection->query($sql);
+ }
+ return $this;
+ }
+
+}
+
130 src/Phpmig/Adapter/File/Flat.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Adapter
+ */
+namespace Phpmig\Adapter\File;
+
+use \Phpmig\Adapter\AdapterInterface,
+ \Phpmig\Migration\Migration;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Flat file adapter
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk
+ */
+class Flat implements AdapterInterface
+{
+ /**
+ * @string
+ */
+ protected $filename = null;
+
+ /**
+ * Construct
+ *
+ * @param string $filename
+ */
+ public function __construct($filename)
+ {
+ $this->filename = $filename;
+ }
+
+ /**
+ * Get all migrated version numbers
+ *
+ * @return array
+ */
+ public function fetchAll()
+ {
+ $versions = file($this->filename, FILE_IGNORE_NEW_LINES);
+ sort($versions);
+ return $versions;
+ }
+
+ /**
+ * Up
+ *
+ * @param Migration $migration
+ * @return AdapterInterface
+ */
+ public function up(Migration $migration)
+ {
+ $versions = $this->fetchAll();
+ if (in_array($migration->getVersion(), $versions)) {
+ return;
+ }
+
+ $versions[] = $migration->getVersion();
+ $this->write($versions);
+ return $this;
+ }
+
+ /**
+ * Down
+ *
+ * @param Migration $migration
+ * @return AdapterInterface
+ */
+ public function down(Migration $migration)
+ {
+ $versions = $this->fetchAll();
+ if (!in_array($migration->getVersion(), $versions)) {
+ return;
+ }
+
+ unset($versions[array_search($migration->getVersion(), $versions)]);
+ $this->write($versions);
+ return $this;
+ }
+
+ /**
+ * Is the schema ready?
+ *
+ * @return bool
+ */
+ public function hasSchema()
+ {
+ return file_exists($this->filename);
+ }
+
+ /**
+ * Create Schema
+ *
+ * @return AdapterInterface
+ */
+ public function createSchema()
+ {
+ if (!is_writeable(dirname($this->filename))) {
+ throw new \InvalidArgumentException(sprintf('The file "%s" is not writeable', $this->filename));
+ }
+
+ if (false === touch($this->filename)) {
+ throw new \InvalidArgumentException(sprintf('The file "%s" could not be written to', $this->filename));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Write to file
+ */
+ protected function write($versions)
+ {
+ if (false === file_put_contents($this->filename, implode("\n", $versions))) {
+ throw new \RuntimeException(sprintf('The file "%s" could not be written to', $this->filename));
+ }
+ }
+}
+
+
+
286 src/Phpmig/Console/Command/AbstractCommand.php
@@ -0,0 +1,286 @@
+<?php
+/**
+ * @package
+ * @subpackage
+ */
+namespace Phpmig\Console\Command;
+
+use Symfony\Component\Console\Command\Command,
+ Symfony\Component\Console\Input\InputInterface,
+ Symfony\Component\Console\Input\InputArgument,
+ Symfony\Component\Console\Output\OutputInterface,
+ Symfony\Component\Config\FileLocator,
+ Phpmig\Migration\Migration,
+ Phpmig\Migration\Migrator,
+ Phpmig\Adapter\AdapterInterface;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Abstract command, contains bootstrapping info
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk>
+ */
+abstract class AbstractCommand extends Command
+{
+ /**
+ * @var \ArrayAccess
+ */
+ protected $container = null;
+
+ /**
+ * @var \Phpmig\Adapter\AdapterInterface
+ */
+ protected $adapter = null;
+
+ /**
+ * @var string
+ */
+ protected $bootstrap = null;
+
+ /**
+ * @var array
+ */
+ protected $migrations = array();
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this->addOption('--bootstrap', '-b', InputArgument::OPTIONAL, 'The bootstrap file to load');
+ }
+
+ /**
+ * Bootstrap phpmig
+ *
+ * @return void
+ */
+ protected function bootstrap(InputInterface $input, OutputInterface $output)
+ {
+ /**
+ * Bootstrap
+ */
+ $bootstrap = $input->getOption('bootstrap');
+
+ if (null === $bootstrap) {
+ $bootstrap = 'phpmig.php';
+ }
+
+ $cwd = getcwd();
+
+ $locator = new FileLocator(array(
+ $cwd . DIRECTORY_SEPARATOR . 'config',
+ ));
+
+ $bootstrap = $locator->locate($bootstrap, $cwd, $first = true);
+ $this->setBootstrap($bootstrap);
+
+ /**
+ * Prevent scope clashes
+ */
+ $func = function() use ($bootstrap) {
+ return require $bootstrap;
+ };
+
+ $container = $func();
+
+ if (!($container instanceof \ArrayAccess)) {
+ throw new RuntimeException($bootstrap . " must return object of type \ArrayAccess");
+ }
+ $this->setContainer($container);
+
+ /**
+ * Adapter
+ */
+ if (!isset($container['phpmig.adapter'])) {
+ throw new RuntimeException($bootstrap . " must return container with service at phpmig.adapter");
+ }
+
+ $adapter = $container['phpmig.adapter'];
+
+ if (!($adapter instanceof \Phpmig\Adapter\AdapterInterface)) {
+ throw new RuntimeException("phpmig.adapter must be an instance of \Phpmig\Adapter\AdapterInterface");
+ }
+
+ if (!$adapter->hasSchema()) {
+ $adapter->createSchema();
+ }
+
+ $this->setAdapter($adapter);
+
+ /**
+ * Migrations
+ */
+ if (!isset($container['phpmig.migrations'])) {
+ throw new RuntimeException($bootstrap . " must return container with array at phpmig.migrations");
+ }
+
+ $migrations = $container['phpmig.migrations'];
+
+ if (!is_array($migrations)) {
+ throw new RuntimeException("phpmig.migrations must be an array of paths to migrations");
+ }
+
+ $versions = array();
+ $names = array();
+ foreach($migrations as $path) {
+ if (!preg_match('/^[0-9]+/', basename($path), $matches)) {
+ throw new \InvalidArgumentException(sprintf('The file "%s" does not have a valid migration filename', $path));
+ }
+
+ $version = $matches[0];
+
+ if (isset($versions[$version])) {
+ throw new \InvalidArgumentException(sprintf('Duplicate migration, "%s" has the same version as "%s"', $path, $versions[$version]));
+ }
+
+ $class = preg_replace('/^[0-9]+_/', '', basename($path));
+ $class = str_replace('_', ' ', $class);
+ $class = ucwords($class);
+ $class = str_replace(' ', '', $class);
+ if (false !== strpos($class, '.')) {
+ $class = substr($class, 0, strpos($class, '.'));
+ }
+
+ if (isset($names[$class])) {
+ throw new \InvalidArgumentException(sprintf(
+ 'Migration "%s" has the same name as "%s"',
+ $path,
+ $names[$class]
+ ));
+ }
+ $names[$class] = $path;
+
+ require_once $path;
+ if (!class_exists($class)) {
+ throw new \InvalidArgumentException(sprintf(
+ 'Could not find class "%s" in file "%s"',
+ $class,
+ $path
+ ));
+ }
+
+ $migration = new $class($version);
+
+ if (!($migration instanceof Migration)) {
+ throw new \InvalidArgumentException(sprintf(
+ 'The class "%s" in file "%s" must extend \Phpmig\Migration\Migration',
+ $class,
+ $path
+ ));
+ }
+
+ $versions[$version] = $migration;
+ }
+
+ ksort($versions);
+
+ /**
+ * Setup migrator
+ */
+ $container['phpmig.migrator'] = $container->share(function() use ($container, $adapter, $output) {
+ return new Migrator($adapter, $container, $output);
+ });
+
+ $this->setMigrations($versions);
+ }
+
+ /**
+ * Set bootstrap
+ *
+ * @var string
+ * @return AbstractCommand
+ */
+ public function setBootstrap($bootstrap)
+ {
+ $this->bootstrap = $bootstrap;
+ return $this;
+ }
+
+ /**
+ * Get bootstrap
+ *
+ * @return string
+ */
+ public function getBootstrap()
+ {
+ return $this->bootstrap;
+ }
+
+ /**
+ * Set migrations
+ *
+ * @param array $migrations
+ * @return AbstractCommand
+ */
+ public function setMigrations(array $migrations)
+ {
+ $this->migrations = $migrations;
+ return $this;
+ }
+
+ /**
+ * Get migrations
+ *
+ * @return array
+ */
+ public function getMigrations()
+ {
+ return $this->migrations;
+ }
+
+ /**
+ * Set container
+ *
+ * @var \ArrayAccess
+ * @return AbstractCommand
+ */
+ public function setContainer(\ArrayAccess $container)
+ {
+ $this->container = $container;
+ return $this;
+ }
+
+ /**
+ * Get container
+ *
+ * @return \ArrayAccess
+ */
+ public function getContainer()
+ {
+ return $this->container;
+ }
+
+ /**
+ * Set adapter
+ *
+ * @param AdapterInterface $adapter
+ * @return AbstractCommand
+ */
+ public function setAdapter(AdapterInterface $adapter)
+ {
+ $this->adapter = $adapter;
+ return $this;
+ }
+
+ /**
+ * Get Adapter
+ *
+ * @return AdapterInterface
+ */
+ public function getAdapter()
+ {
+ return $this->adapter;
+ }
+
+}
+
+
74 src/Phpmig/Console/Command/DownCommand.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Console
+ */
+namespace Phpmig\Console\Command;
+
+use Symfony\Component\Console\Input\InputInterface,
+ Symfony\Component\Console\Input\InputArgument,
+ Symfony\Component\Console\Output\OutputInterface,
+ Symfony\Component\Config\FileLocator;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Down command
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk>
+ */
+class DownCommand extends AbstractCommand
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ parent::configure();
+
+ $this->setName('down')
+ ->addArgument('version', InputArgument::REQUIRED, 'The version number for the migration')
+ ->setDescription('Revert a specific migration')
+ ->setHelp(<<<EOT
+The <info>down</info> command reverts a specific migration
+
+<info>phpmig down 20111018185412</info>
+
+EOT
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->bootstrap($input, $output);
+
+ $migrations = $this->getMigrations();
+ $versions = $this->getAdapter()->fetchAll();
+
+ $version = $input->getArgument('version');
+
+ if (!in_array($version, $versions)) {
+ return;
+ }
+
+ if (!isset($migrations[$version])) {
+ return;
+ }
+
+ $container = $this->getContainer();
+ $container['phpmig.migrator']->down($migrations[$version]);
+ }
+}
+
+
+
122 src/Phpmig/Console/Command/GenerateCommand.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Console
+ */
+namespace Phpmig\Console\Command;
+
+use Symfony\Component\Console\Input\InputInterface,
+ Symfony\Component\Console\Input\InputArgument,
+ Symfony\Component\Console\Output\OutputInterface,
+ Symfony\Component\Config\FileLocator;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Generate command
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk>
+ */
+class GenerateCommand extends AbstractCommand
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ parent::configure();
+
+ $this->setName('generate')
+ ->addArgument('name', InputArgument::REQUIRED, 'The name for the migration')
+ ->addArgument('path', InputArgument::REQUIRED, 'The directory in which to put the migration')
+ ->setDescription('Generate a new migration')
+ ->setHelp(<<<EOT
+The <info>generate</info> command creates a new migration with the name and path specified
+
+<info>phpmig generate Dave ./migrations</info>
+
+EOT
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $path = $input->getArgument('path');
+ $locator = new FileLocator(array());
+ $path = $locator->locate($path, getcwd(), $first = true);
+
+ if (!is_writeable($path)) {
+ throw new \InvalidArgumentException(sprintf(
+ 'The directory "%s" is not writeable',
+ $path
+ ));
+ }
+
+ $path = realpath($path);
+
+ $className = $input->getArgument('name');
+ $basename = date('YmdHis') . '_' . $className . '.php';
+
+ $path = $path . DIRECTORY_SEPARATOR . $basename;
+
+ if (file_exists($path)) {
+ throw new \InvalidArgumentException(sprintf(
+ 'The file "%s" already exists',
+ $path
+ ));
+ }
+
+ $contents = <<<PHP
+<?php
+
+use Phpmig\Migration\Migration;
+
+class $className extends Migration
+{
+ /**
+ * Do the migration
+ */
+ public function up()
+ {
+
+ }
+
+ /**
+ * Undo the migration
+ */
+ public function down()
+ {
+
+ }
+}
+
+PHP;
+
+ if (false === file_put_contents($path, $contents)) {
+ throw new \RuntimeException(sprintf(
+ 'The file "%s" could not be written to',
+ $path
+ ));
+ }
+
+ $output->writeln(
+ '<info>+f</info> ' .
+ '.' . str_replace(getcwd(), '', $path)
+ );
+
+ return;
+ }
+}
+
+
+
137 src/Phpmig/Console/Command/InitCommand.php
@@ -0,0 +1,137 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Console
+ */
+namespace Phpmig\Console\Command;
+
+use Symfony\Component\Console\Input\InputInterface,
+ Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Init command
+ *
+ * @author Dave Marshall <david.marshall@bskyb.com>
+ */
+class InitCommand extends AbstractCommand
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this->setName('init')
+ ->setDescription('Initialise this directory for use with phpmig')
+ ->setHelp(<<<EOT
+The <info>init</info> command creates a skeleton bootstrap file and a migrations directory
+
+<info>phpmig init</info>
+
+EOT
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $cwd = getcwd();
+ $bootstrap = $cwd . DIRECTORY_SEPARATOR . 'phpmig.php';
+ $relative = 'migrations';
+ $migrations = $cwd . DIRECTORY_SEPARATOR . $relative;
+
+ $this->initMigrationsDir($migrations, $output);
+ $this->initBootstrap($bootstrap, $relative, $output);
+ }
+
+ /**
+ * Create migrations dir
+ *
+ * @param $path
+ * @return void
+ */
+ protected function initMigrationsDir($migrations, OutputInterface $output)
+ {
+ if (file_exists($migrations) && is_dir($migrations)) {
+ $output->writeln(
+ '<info>--</info> ' .
+ str_replace(getcwd(), '.', $migrations) . ' already exists -' .
+ ' <comment>Place your migration files in here</comment>'
+ );
+ return;
+ }
+
+ if (false === mkdir($migrations)) {
+ throw new \RuntimeException(sprintf('Could not create directory "%s"', $migrations));
+ }
+
+ $output->writeln(
+ '<info>+d</info> ' .
+ str_replace(getcwd(), '.', $migrations) .
+ ' <comment>Place your migration files in here</comment>'
+ );
+ return;
+ }
+
+ /**
+ * Create bootstrap
+ *
+ * @param string $bootstrap where to put bootstrap file
+ * @param string $migrations path to migrations dir relative to bootstrap
+ * @return void
+ */
+ protected function initBootstrap($bootstrap, $migrations, OutputInterface $output)
+ {
+ if (file_exists($bootstrap)) {
+ throw new \RuntimeException(sprintf('The file "%s" already exists', $bootstrap));
+ }
+
+ if (!is_writeable(dirname($bootstrap))) {
+ throw new \RuntimeException(sprintf('THe file "%s" is not writeable', $bootstrap));
+ }
+
+ $contents = <<<PHP
+<?php
+
+use \Phpmig\Adapter,
+ \Phpmig\Pimple\Pimple;
+
+\$container = new Pimple();
+
+\$container['phpmig.adapter'] = \$container->share(function() {
+ // replace this with a better Phpmig\Adapter\AdapterInterface
+ return new Adapter\File\Flat(__DIR__ . DIRECTORY_SEPARATOR . '$migrations/.migrations.log');
+});
+
+\$container['phpmig.migrations'] = function() {
+ return glob(__DIR__ . DIRECTORY_SEPARATOR . '$migrations/*.php');
+};
+
+return \$container;
+PHP;
+
+ if (false === file_put_contents($bootstrap, $contents)) {
+ throw new \RuntimeException('THe file "%s" could not be written to', $bootstrap);
+ }
+
+ $output->writeln(
+ '<info>+f</info> ' .
+ str_replace(getcwd(), '.', $bootstrap) .
+ ' <comment>Create services in here</comment>'
+ );
+ return;
+ }
+}
+
+
+
112 src/Phpmig/Console/Command/MigrateCommand.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Console
+ */
+namespace Phpmig\Console\Command;
+
+use Symfony\Component\Console\Input\InputInterface,
+ Symfony\Component\Console\Input\InputArgument,
+ Symfony\Component\Console\Output\OutputInterface,
+ Symfony\Component\Config\FileLocator;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Migrate command
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk>
+ */
+class MigrateCommand extends AbstractCommand
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ parent::configure();
+
+ $this->setName('migrate')
+ ->addOption('--target', '-t', InputArgument::OPTIONAL, 'The version number to migrate to')
+ ->setDescription('Run all migrations')
+ ->setHelp(<<<EOT
+The <info>migrate</info> command runs all available migrations, optionally up to a specific version
+
+<info>phpmif migrate</info>
+<info>phpmig migrate -t 20111018185412</info>
+
+EOT
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->bootstrap($input, $output);
+
+ $migrations = $this->getMigrations();
+ $versions = $this->getAdapter()->fetchAll();
+
+ $version = $input->getOption('target');
+
+ ksort($migrations);
+ sort($versions);
+
+ if (!empty($versions)) {
+ $current = $versions[count($versions) - 1];
+ } else {
+ $current = 0;
+ }
+
+ if (null !== $version) {
+ if (0 != $version && !isset($migrations[$version])) {
+ return;
+ }
+ } else {
+ $version = max(array_merge($versions, array_keys($migrations)));
+ }
+
+ $direction = $version > $current ? 'up' : 'down';
+
+ if ($direction == 'down') {
+ /**
+ * Run downs first
+ */
+ krsort($migrations);
+ foreach($migrations as $migration) {
+ if ($migration->getVersion() <= $version) {
+ break;
+ }
+
+ if (in_array($migration->getVersion(), $versions)) {
+ $container = $this->getContainer();
+ $container['phpmig.migrator']->down($migration);
+ }
+ }
+ }
+
+ ksort($migrations);
+ foreach($migrations as $migration) {
+ if ($migration->getVersion() > $version) {
+ break;
+ }
+
+ if (!in_array($migration->getVersion(), $versions)) {
+ $container = $this->getContainer();
+ $container['phpmig.migrator']->up($migration);
+ }
+ }
+ }
+}
+
+
+
87 src/Phpmig/Console/Command/StatusCommand.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Console
+ */
+namespace Phpmig\Console\Command;
+
+use Symfony\Component\Console\Input\InputInterface,
+ Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Status command
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk>
+ */
+class StatusCommand extends AbstractCommand
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ parent::configure();
+
+ $this->setName('status')
+ ->setDescription('Show the up/down status of all migrations')
+ ->setHelp(<<<EOT
+The <info>status</info> command prints a list of all migrations, along with their current status
+
+<info>phpmig status</info>
+
+EOT
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->bootstrap($input, $output);
+ $output->writeln("");
+ $output->writeln(" Status Migration ID Migration Name ");
+ $output->writeln("-----------------------------------------");
+
+ $versions = $this->getAdapter()->fetchAll();
+ foreach($this->getMigrations() as $migration) {
+
+ if (in_array($migration->getVersion(), $versions)) {
+ $status = " <info>up</info> ";
+ unset($versions[array_search($migration->getVersion(), $versions)]);
+ } else {
+ $status = " <error>down</error> ";
+ }
+
+ $output->writeln(
+ $status .
+ sprintf(" %14s ", $migration->getVersion()) .
+ " <comment>" . $migration->getName() . "</comment>"
+ );
+ }
+
+ foreach($versions as $missing) {
+ $output->writeln(
+ ' <error>up</error> ' .
+ sprintf(" %14d ", $missing) .
+ ' <error>** MISSING **</error> '
+ );
+ }
+
+ // print status
+ $output->writeln("");
+ return;
+ }
+}
+
+
+
74 src/Phpmig/Console/Command/UpCommand.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Console
+ */
+namespace Phpmig\Console\Command;
+
+use Symfony\Component\Console\Input\InputInterface,
+ Symfony\Component\Console\Input\InputArgument,
+ Symfony\Component\Console\Output\OutputInterface,
+ Symfony\Component\Config\FileLocator;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Up command
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk>
+ */
+class UpCommand extends AbstractCommand
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ parent::configure();
+
+ $this->setName('up')
+ ->addArgument('version', InputArgument::REQUIRED, 'The version number for the migration')
+ ->setDescription('Run a specific migration')
+ ->setHelp(<<<EOT
+The <info>up</info> command runs a specific migration
+
+<info>phpmig up 20111018185121</info>
+
+EOT
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->bootstrap($input, $output);
+
+ $migrations = $this->getMigrations();
+ $versions = $this->getAdapter()->fetchAll();
+
+ $version = $input->getArgument('version');
+
+ if (in_array($version, $versions)) {
+ return;
+ }
+
+ if (!isset($migrations[$version])) {
+ return;
+ }
+
+ $container = $this->getContainer();
+ $container['phpmig.migrator']->up($migrations[$version]);
+ }
+}
+
+
+
69 src/Phpmig/Console/PhpmigApplication.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Console
+ */
+namespace Phpmig\Console;
+
+use Symfony\Component\Console\Application,
+ Symfony\Component\Console\Input\InputOption,
+ Symfony\Component\Console\Input\InputInterface,
+ Symfony\Component\Console\Output\OutputInterface,
+ Symfony\Component\Yaml\Yaml,
+ Symfony\Component\Config\FileLocator,
+ Phpmig\Console\Command;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The main phpmig application
+ *
+ * @author Dave Marshall <david.marshall@bskyb.com>
+ */
+class PhpmigApplication extends Application
+{
+ /**
+ * @var \ArrayAccess
+ */
+ protected $container = null;
+
+ /**
+ * Constructor
+ */
+ public function __construct($version = 'dev') {
+ parent::__construct('phpmig', $version);
+
+ $this->addCommands(array(
+ new Command\InitCommand(),
+ new Command\StatusCommand(),
+ new Command\GenerateCommand(),
+ new Command\UpCommand(),
+ new Command\DownCommand(),
+ new Command\MigrateCommand(),
+ ));
+ }
+
+ /**
+ * Runs the current application.
+ *
+ * Lots of logic in here that needs abstracting out.
+ *
+ * @param InputInterface $input An Input instance
+ * @param OutputInterface $output An Output instance
+ *
+ * @return integer 0 if everything went fine, or an error code
+ */
+ public function doRun(InputInterface $input, OutputInterface $output)
+ {
+ return parent::doRun($input, $output);
+ }
+
+}
+
160 src/Phpmig/Migration/Migration.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Migration
+ */
+namespace Phpmig\Migration;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Migration
+ *
+ * A migration describes the changes that should be made (or unmade)
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk>
+ */
+class Migration
+{
+ /**
+ * @var int
+ */
+ protected $version = null;
+
+ /**
+ * @var \ArrayAccess
+ */
+ protected $container = null;
+
+ /**
+ * @var OutputInterface
+ */
+ protected $output = null;
+
+ /**
+ * Constructor
+ *
+ * @param int $version
+ */
+ final public function __construct($version)
+ {
+ $this->version = $version;
+ $this->init();
+ }
+
+ /**
+ * init
+ *
+ * @return void
+ */
+ protected function init()
+ {
+ return;
+ }
+
+ /**
+ * Do the migration
+ *
+ * @return void
+ */
+ public function up()
+ {
+ return;
+ }
+
+ /**
+ * Undo the migration
+ *
+ * @return void
+ */
+ public function down()
+ {
+ return;
+ }
+
+ /**
+ * Get Version
+ *
+ * @return int
+ */
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * Set version
+ *
+ * @param int $version
+ * @return Migration
+ */
+ public function setVersion($version)
+ {
+ $this->version = $version;
+ return $this;
+ }
+
+ /**
+ * Get name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return get_class($this);
+ }
+
+ /**
+ * Get Container
+ *
+ * @return \ArrayAccess
+ */
+ public function getContainer()
+ {
+ return $this->container;
+ }
+
+ /**
+ * Set Container
+ *
+ * @param \ArrayAccess $container
+ * @return Migrator
+ */
+ public function setContainer(\ArrayAccess $container)
+ {
+ $this->container = $container;
+ return $this;
+ }
+
+ /**
+ * Get Output
+ *
+ * @return OutputInterface
+ */
+ public function getOutput()
+ {
+ return $this->output;
+ }
+
+ /**
+ * Set Output
+ *
+ * @param OutputInterface $output
+ * @return Migrator
+ */
+ public function setOutput(OutputInterface $output)
+ {
+ $this->output = $output;
+ return $this;
+ }
+}
+
+
+
183 src/Phpmig/Migration/Migrator.php
@@ -0,0 +1,183 @@
+<?php
+/**
+ * @package Phpmig
+ * @subpackage Phpmig\Migration
+ */
+namespace Phpmig\Migration;
+
+use Phpmig\Adapter\AdapterInterface,
+ Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Migrator
+ *
+ * Decided what to migrate and migrates
+ *
+ * @author Dave Marshall <david.marshall@atstsolutions.co.uk>
+ */
+class Migrator
+{
+ /**
+ * @var \ArrayAccess
+ */
+ protected $container = null;
+
+ /**
+ * @var AdapterInterface
+ */
+ protected $adapter = null;
+
+ /**
+ * @var OutputInterface
+ */
+ protected $output = null;
+
+ /**
+ * Constructor
+ *
+ * @param AdapterInterface $adapter
+ * @param \ArrayAccess $container
+ */
+ public function __construct(AdapterInterface $adapter, \ArrayAccess $container, OutputInterface $output)
+ {
+ $this->container = $container;
+ $this->adapter = $adapter;
+ $this->output = $output;
+ }
+
+ /**
+ * Run the up method on a migration
+ *
+ * @param Migration $migration
+ * @return void
+ */
+ public function up(Migration $migration)
+ {
+ $this->run($migration, 'up');
+ return;
+ }
+
+ /**
+ * Run the down method on a migration
+ *
+ * @param Migration $migration
+ * @return void
+ */
+ public function down(Migration $migration)
+ {
+ $this->run($migration, 'down');
+ return;
+ }
+
+ /**
+ * Run a migration in a particular direction
+ *
+ * @param Migration $migration
+ * @param string $direction
+ * @return void
+ */
+ protected function run(Migration $migration, $direction = 'up')
+ {
+ $direction = ($direction == 'down' ? 'down' :'up');
+ $this->getOutput()->writeln(sprintf(
+ ' == <info>' .
+ $migration->getVersion() . ' ' .
+ $migration->getName() . '</info> ' .
+ '<comment>' .
+ ($direction == 'up' ? 'migrating' : 'reverting') .
+ '</comment>'
+ ));
+ $start = microtime(1);
+ $migration->setContainer($this->getContainer());
+ $migration->{$direction}();
+ $this->getAdapter()->{$direction}($migration);
+ $end = microtime(1);
+ $this->getOutput()->writeln(sprintf(
+ ' == <info>' .
+ $migration->getVersion() . ' ' .
+ $migration->getName() . '</info> ' .
+ '<comment>' .
+ ($direction == 'up' ? 'migrated ' : 'reverted ') .
+ sprintf("%.4fs", $end - $start) .
+ '</comment>'
+ ));
+ }
+
+ /**
+ * Get Container
+ *
+ * @return \ArrayAccess
+ */
+ public function getContainer()
+ {
+ return $this->container;
+ }
+
+ /**
+ * Set Container
+ *
+ * @param \ArrayAccess $container
+ * @return Migrator
+ */
+ public function setContainer(\ArrayAccess $container)
+ {
+ $this->container = $container;
+ return $this;
+ }
+
+ /**
+ * Get Adapter
+ *
+ * @return AdapterInterface
+ */
+ public function getAdapter()
+ {
+ return $this->adapter;
+ }
+
+ /**
+ * Set Adapter
+ *
+ * @param AdapterInterface $adapter
+ * @return Migrator
+ */
+ public function setAdapter(AdapterInterface $adapter)
+ {
+ $this->adapter = $adapter;
+ return $this;
+ }
+
+ /**
+ * Get Output
+ *
+ * @return OutputInterface
+ */
+ public function getOutput()
+ {
+ return $this->output;
+ }
+
+ /**
+ * Set Output
+ *
+ * @param OutputInterface $output
+ * @return Migrator
+ */
+ public function setOutput(OutputInterface $output)
+ {
+ $this->output = $output;
+ return $this;
+ }
+}
+
+
+
150 src/Phpmig/Pimple/Pimple.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace Phpmig\Pimple;
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 Fabien Potencier
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Pimple main class.
+ *
+ * @package pimple
+ * @author Fabien Potencier
+ */
+class Pimple implements \ArrayAccess
+{
+ private $values = array();
+
+ /**
+ * Sets a parameter or an object.
+ *
+ * Objects must be defined as Closures.
+ *
+ * Allowing any PHP callable leads to difficult to debug problems
+ * as function names (strings) are callable (creating a function with
+ * the same a name as an existing parameter would break your container).
+ *
+ * @param string $id The unique identifier for the parameter or object
+ * @param mixed $value The value of the parameter or a closure to defined an object
+ */
+ function offsetSet($id, $value)
+ {
+ $this->values[$id] = $value;
+ }
+
+ /**
+ * Gets a parameter or an object.
+ *
+ * @param string $id The unique identifier for the parameter or object
+ *
+ * @return mixed The value of the parameter or an object
+ *
+ * @throws InvalidArgumentException if the identifier is not defined
+ */
+ function offsetGet($id)
+ {
+ if (!array_key_exists($id, $this->values)) {
+ throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
+ }
+
+ return $this->values[$id] instanceof \Closure ? $this->values[$id]($this) : $this->values[$id];
+ }
+
+ /**
+ * Checks if a parameter or an object is set.
+ *
+ * @param string $id The unique identifier for the parameter or object
+ *
+ * @return Boolean
+ */
+ function offsetExists($id)
+ {
+ return isset($this->values[$id]);
+ }
+
+ /**
+ * Unsets a parameter or an object.
+ *
+ * @param string $id The unique identifier for the parameter or object
+ */
+ function offsetUnset($id)
+ {
+ unset($this->values[$id]);
+ }
+
+ /**
+ * Returns a closure that stores the result of the given closure for
+ * uniqueness in the scope of this instance of Pimple.
+ *
+ * @param \Closure $callable A closure to wrap for uniqueness
+ *
+ * @return \Closure The wrapped closure
+ */
+ function share(\Closure $callable)
+ {
+ return function ($c) use ($callable) {
+ static $object;
+
+ if (is_null($object)) {
+ $object = $callable($c);
+ }
+
+ return $object;
+ };
+ }
+
+ /**
+ * Protects a callable from being interpreted as a service.
+ *
+ * This is useful when you want to store a callable as a parameter.
+ *
+ * @param \Closure $callable A closure to protect from being evaluated
+ *
+ * @return \Closure The protected closure
+ */
+ function protect(\Closure $callable)
+ {
+ return function ($c) use ($callable) {
+ return $callable;
+ };
+ }
+
+ /**
+ * Gets a parameter or the closure defining an object.
+ *
+ * @param string $id The unique identifier for the parameter or object
+ *
+ * @return mixed The value of the parameter or the closure defining an object
+ *
+ * @throws InvalidArgumentException if the identifier is not defined
+ */
+ function raw($id)
+ {
+ if (!isset($this->values[$id])) {
+ throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
+ }
+
+ return $this->values[$id];
+ }
+}
34 src/Phpmig/autoload.php.dist
@@ -0,0 +1,34 @@
+<?php
+
+use Symfony\Component\ClassLoader\UniversalClassLoader;
+
+/**
+ * This file is part of phpmig
+ *
+ * Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+
+if (is_dir(__DIR__ . '/../../vendor/Symfony')) {
+ $symfonyDir = __DIR__ . '/../../vendor/';
+ require_once $symfonyDir . "Symfony/Component/ClassLoader/UniversalClassLoader.php";
+} else {
+ $symfonyDir = "@@PHP_DIR@@";
+ require_once "Symfony/Component/ClassLoader/UniversalClassLoader.php";
+}
+
+$loader = new UniversalClassLoader();
+$loader->registerNamespaces(array(
+ 'Phpmig' => __DIR__ . '/../',
+));
+
+$loader->registerNamespaces(array(
+ 'Symfony\Component\Console' => $symfonyDir,
+ 'Symfony\Component\ClassLoader' => $symfonyDir,
+ 'Symfony\Component\Config' => $symfonyDir,
+));
+
+$loader->register();
0  vendor/Symfony/Component/.empty
No changes.
Please sign in to comment.
Something went wrong with that request. Please try again.