├── .gitignore ├── tests ├── schema5 │ ├── 002_drop.sql │ └── 001_create.sql ├── schema3 │ ├── 1_bar.sql │ └── 1_foo.sql ├── schema1 │ └── 1_test.sql ├── schema2 │ ├── 1_test.sql │ └── 2_test.sql ├── schema4 │ └── 1_foo.sql └── DBDeployPHP │ └── DBDeployTest.php ├── phpunit.xml ├── .travis.yml ├── composer.json ├── bin └── dbdeploy ├── src └── DBDeployPHP │ ├── MigrationStatus.php │ ├── Command │ └── MigrateCommand.php │ └── DBDeploy.php ├── README.md └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | -------------------------------------------------------------------------------- /tests/schema5/002_drop.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE foo; 2 | -------------------------------------------------------------------------------- /tests/schema3/1_bar.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE foo (id INT); 2 | -------------------------------------------------------------------------------- /tests/schema3/1_foo.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE foo (id INT); 2 | -------------------------------------------------------------------------------- /tests/schema1/1_test.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE foo (id INTEGER); 2 | -------------------------------------------------------------------------------- /tests/schema2/1_test.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE foo (id INTEGER); 2 | -------------------------------------------------------------------------------- /tests/schema2/2_test.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE bar (id INTEGER); 2 | -------------------------------------------------------------------------------- /tests/schema5/001_create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE foo (id INTEGER); 2 | -------------------------------------------------------------------------------- /tests/schema4/1_foo.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE foo (id INT); 2 | --//@UNDO 3 | DROP TABLE foo; 4 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests/ 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | 8 | env: 9 | - DATABASE_URL="mysql://travis@127.0.0.1/dbdeploy" 10 | 11 | before_script: 12 | - mysql -uroot -e "CREATE DATABASE dbdeploy" 13 | - composer install 14 | 15 | script: phpunit 16 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beberlei/dbdeploy-php", 3 | "license": "MIT", 4 | "autoload": { 5 | "psr-0": {"DBDeployPHP": "src/"} 6 | }, 7 | "require": { 8 | "doctrine/dbal": "~2.5", 9 | "php": "~5.4" 10 | }, 11 | "bin": ["bin/dbdeploy"] 12 | } 13 | -------------------------------------------------------------------------------- /bin/dbdeploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | add(new Command\MigrateCommand()); 34 | $application->run(); 35 | -------------------------------------------------------------------------------- /src/DBDeployPHP/MigrationStatus.php: -------------------------------------------------------------------------------- 1 | all = $all; 25 | $this->applied = $applied; 26 | $this->apply = $apply; 27 | } 28 | 29 | public function getAllMigrations() 30 | { 31 | return $this->all; 32 | } 33 | 34 | public function getApplyMigrations() 35 | { 36 | return $this->apply; 37 | } 38 | 39 | public function getAppliedMigrations() 40 | { 41 | return $this->applied; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/DBDeployPHP/Command/MigrateCommand.php: -------------------------------------------------------------------------------- 1 | setName('migrate') 20 | ->setDescription('Migrate a given database to the latest version.') 21 | ->addArgument('schema-dir', InputArgument::REQUIRED) 22 | ->addOption('dsn', 'd', InputOption::VALUE_REQUIRED) 23 | ; 24 | } 25 | 26 | protected function execute(InputInterface $input, OutputInterface $output) 27 | { 28 | $directory = $input->getArgument('schema-dir'); 29 | 30 | if (strpos($directory, '/') !== 0) { 31 | $directory = getcwd() . '/' . $directory; 32 | } 33 | 34 | if (!file_exists($directory)) { 35 | throw new RuntimeException(sprintf("Schema directory does not exist at '%s'.", $directory)); 36 | } 37 | 38 | $dsn = $input->getOption('dsn'); 39 | 40 | if (isset($_SERVER['DATABASE_URL'])) { 41 | $dsn = $_SERVER['DATABASE_URL']; 42 | } 43 | 44 | if (!$dsn) { 45 | throw new RuntimeException("Missing environment variable DATABASE_URL in format mysql://user:password@host/database"); 46 | } 47 | 48 | $connection = DriverManager::getConnection(array('url' => $_SERVER['DATABASE_URL'])); 49 | $migrator = new DBDeploy($connection, $directory); 50 | 51 | $output->writeln(sprintf("Reading change scripts from directory %s... \n", $directory)); 52 | 53 | $status = $migrator->getCurrentStatus(); 54 | 55 | $appliedString = $status->getAppliedMigrations() ? implode(', ', array_keys($status->getAppliedMigrations())) : '(none)'; 56 | $applyString = $status->getApplyMigrations() ? implode(', ', array_keys($status->getApplyMigrations())) : '(none)'; 57 | 58 | $output->writeln(sprintf("Changes currently applied to database:\n %s\n", $appliedString)); 59 | $output->writeln(sprintf("To be applied:\n %s", $applyString)); 60 | 61 | $migrator->apply($status); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DBDeploy PHP 2 | 3 | [![Build Status](https://travis-ci.org/beberlei/dbdeploy-php.svg?branch=master)](https://travis-ci.org/beberlei/dbdeploy-php) 4 | 5 | This is a clone of [DBDeploy](http://dbdeploy.com) for PHP using Doctrine DBAL 6 | as database abstraction. 7 | 8 | It supports only a limited set of functionality of the original Java based 9 | tool, but enough for one particular workflow to function perfectly. 10 | 11 | Why? This is extracted from a testsuite where it was used for setting up the 12 | schema of the testing database. It is also much easier to setup than DBDeploy. 13 | 14 | ## Workflow Assumptions 15 | 16 | * Only .sql file based migrations, format `_.sql` 17 | * Requires using backwards-compatible database changes, no support undo/down 18 | migrations. Especially 19 | * Avoid dropping stuff 20 | * Added column must either allow NULL or have a default value 21 | * Orders migrations (natural sort) using number prefixes in files. Use 22 | `YYmmddHHii_.sql` format to allow branching without conflicts. 23 | * Single database vendor per directory, use multiple for apps with different 24 | vendor support. 25 | * Creates table `changelog` that contains current state of already applied migrations. 26 | 27 | ## API 28 | 29 | The API just has one method: `migrate()`: 30 | 31 | ```php 32 | migrate(); 39 | ``` 40 | ## CLI 41 | 42 | You need environment variable `DATABASE_URL` present with the format: `mysql://user:password@host/dbname`: 43 | 44 | $ php vendor/bin/dbdeploy-migrate.php src/schema 45 | 46 | ## Limitations 47 | 48 | Currently only works with MySQL. 49 | 50 | ## License 51 | 52 | The MIT License (MIT) 53 | 54 | Copyright (c) 2015 Benjamin Eberlei 55 | 56 | Permission is hereby granted, free of charge, to any person obtaining a copy 57 | of this software and associated documentation files (the "Software"), to deal 58 | in the Software without restriction, including without limitation the rights 59 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 60 | copies of the Software, and to permit persons to whom the Software is 61 | furnished to do so, subject to the following conditions: 62 | 63 | The above copyright notice and this permission notice shall be included in 64 | all copies or substantial portions of the Software. 65 | 66 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 67 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 68 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 69 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 70 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 71 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 72 | THE SOFTWARE. 73 | -------------------------------------------------------------------------------- /tests/DBDeployPHP/DBDeployTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped('Requires DBDEPLOY_TEST_DATABASE_URL env variable for a connection (not directly to a database)'); 19 | } 20 | 21 | $connection = DriverManager::getConnection(array( 22 | 'url' => $_SERVER['DBDEPLOY_TEST_DATABASE_URL'], 23 | )); 24 | $connection->exec('CREATE DATABASE IF NOT EXISTS ' . self::$databaseName); 25 | $connection->exec('USE ' . self::$databaseName); 26 | 27 | return $connection; 28 | } 29 | 30 | /** 31 | * @test 32 | */ 33 | public function it_migrates() 34 | { 35 | $connection = $this->createConnection(); 36 | $schemaManager = $connection->getSchemaManager(); 37 | 38 | $schemaManager->tryMethod('dropTable', 'foo'); 39 | $schemaManager->tryMethod('dropTable', 'bar'); 40 | $schemaManager->tryMethod('dropTable', 'changelog'); 41 | 42 | $deploy = new DBDeploy($connection, __DIR__ . '/../schema1'); 43 | $deploy->migrate(); 44 | 45 | $this->assertEquals(array('changelog', 'foo'), $schemaManager->listTableNames()); 46 | 47 | $deploy = new DBDeploy($connection, __DIR__ . '/../schema2'); 48 | $deploy->migrate(); 49 | 50 | $this->assertEquals(array('bar', 'changelog', 'foo'), $schemaManager->listTableNames()); 51 | } 52 | 53 | /** 54 | * @test 55 | */ 56 | public function it_requires_schema_manager() 57 | { 58 | $connection = DriverManager::getConnection(array('url' => 'sqlite://memory')); 59 | 60 | $this->setExpectedException('RuntimeException', 'SchemaDirectory '); 61 | 62 | new DBDeploy($connection, __DIR__ . '/doesnotexist'); 63 | } 64 | 65 | /** 66 | * @test 67 | */ 68 | public function it_disallows_duplicate_revisions() 69 | { 70 | $connection = $this->createConnection(); 71 | 72 | $this->setExpectedException('RuntimeException', "Duplicate revision number '1' is not allowed."); 73 | 74 | $deploy = new DBDeploy($connection, __DIR__ . '/../schema3'); 75 | $deploy->migrate(); 76 | } 77 | 78 | /** 79 | * @test 80 | */ 81 | public function it_disallows_undo_dbdeploy_files() 82 | { 83 | $connection = $this->createConnection(); 84 | 85 | $this->setExpectedException('RuntimeException', 'No support for DBDeploy "--//@UNDO" feature.'); 86 | 87 | $deploy = new DBDeploy($connection, __DIR__ . '/../schema4'); 88 | $deploy->migrate(); 89 | } 90 | 91 | /** 92 | * @test 93 | */ 94 | public function it_natuarlly_sorts() 95 | { 96 | $connection = $this->createConnection(); 97 | $schemaManager = $connection->getSchemaManager(); 98 | 99 | $schemaManager->tryMethod('dropTable', 'foo'); 100 | $schemaManager->tryMethod('dropTable', 'bar'); 101 | $schemaManager->tryMethod('dropTable', 'changelog'); 102 | 103 | $deploy = new DBDeploy($connection, __DIR__ . '/../schema5'); 104 | $deploy->migrate(); 105 | 106 | $migrations = array_map(function ($row) { 107 | return $row['description']; 108 | }, $connection->fetchAll('SELECT description FROM changelog')); 109 | 110 | $this->assertEquals(array('changelog'), $schemaManager->listTableNames()); 111 | $this->assertEquals(array('001_create.sql', '002_drop.sql'), $migrations); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/DBDeployPHP/DBDeploy.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 46 | $this->schemaDirectory = $schemaDirectory; 47 | } 48 | 49 | /** 50 | * Return Current Migration Status of the database. 51 | * 52 | * @return MigrationStatus 53 | */ 54 | public function getCurrentStatus() 55 | { 56 | $schemaManager = $this->connection->getSchemaManager(); 57 | $tables = $schemaManager->listTableNames(); 58 | 59 | if (!in_array('changelog', $tables)) { 60 | $table = new \Doctrine\DBAL\Schema\Table('changelog'); 61 | $table->addColumn('change_number', 'integer'); 62 | $table->addColumn('complete_dt', 'datetime', array( 63 | 'columnDefinition' => 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP', 64 | )); 65 | $table->addColumn('applied_by', 'string', array('length' => 100)); 66 | $table->addcolumn('description', 'string', array('length' => 500)); 67 | $table->setPrimaryKey(array('change_number')); 68 | 69 | $schemaManager->createTable($table); 70 | } 71 | 72 | $allMigrations = $this->getAllMigrations($this->schemaDirectory); 73 | $appliedMigrations = $this->getAppliedMigrations(); 74 | $applyMigrations = array(); 75 | 76 | foreach ($allMigrations as $revision => $data) { 77 | if (!isset($appliedMigrations[$revision])) { 78 | $applyMigrations[$revision] = $data; 79 | } 80 | } 81 | 82 | return new MigrationStatus($allMigrations, $appliedMigrations, $applyMigrations); 83 | } 84 | 85 | /** 86 | * Migrate database to new version comparing changelog table and schema directory. 87 | * 88 | * @return array 89 | */ 90 | public function migrate() 91 | { 92 | $status = $this->getCurrentStatus(); 93 | $this->apply($status); 94 | 95 | return $status->getApplyMigrations(); 96 | } 97 | 98 | /** 99 | * Apply a migration status with unapplied changes to the database. 100 | * 101 | * @param MigrationStatus $status 102 | */ 103 | public function apply(MigrationStatus $status) 104 | { 105 | foreach ($status->getApplyMigrations() as $revision => $data) { 106 | $this->connection->exec($data['sql']); 107 | $this->connection->insert( 108 | 'changelog', 109 | array( 110 | 'change_number' => $data['change_number'], 111 | 'description' => $data['description'], 112 | 'applied_by' => $data['applied_by'] 113 | ) 114 | ); 115 | } 116 | } 117 | 118 | private function getAllMigrations($path) 119 | { 120 | $files = glob($path . '/*.sql'); 121 | $migrations = array(); 122 | 123 | foreach ($files as $file) { 124 | $basefile = basename($file); 125 | $sql = file_get_contents($file); 126 | 127 | $revision = $this->getRevision($basefile); 128 | 129 | if (isset($migrations[$revision])) { 130 | throw new \RuntimeException(sprintf("Duplicate revision number '%d' is not allowed.", $revision)); 131 | } 132 | 133 | if (strpos($sql, '--//@UNDO') !== false) { 134 | throw new \RuntimeException('No support for DBDeploy "--//@UNDO" feature.'); 135 | } 136 | 137 | $migrations[$revision] = array( 138 | 'change_number' => $revision, 139 | 'sql' => $sql, 140 | 'file' => $file, 141 | 'description' => $basefile, 142 | 'applied_by' => $this->connection->getUsername(), 143 | ); 144 | } 145 | 146 | ksort($migrations, SORT_NATURAL); 147 | 148 | return $migrations; 149 | } 150 | 151 | private function getAppliedMigrations() 152 | { 153 | $appliedMigrations = array(); 154 | 155 | $sql = 'SELECT * FROM changelog'; 156 | $stmt = $this->connection->executeQuery($sql); 157 | 158 | while ($row = $stmt->fetch()) { 159 | $revision = $this->getRevision($row['description']); 160 | 161 | if (isset($appliedMigrations[$revision])) { 162 | throw new \RuntimeException(sprintf("Duplicate revision number '%d' is not allowed.", $revision)); 163 | } 164 | 165 | $appliedMigrations[$revision] = $row; 166 | } 167 | 168 | return $appliedMigrations; 169 | } 170 | 171 | private function getRevision($basefile) 172 | { 173 | if (preg_match('((^[0-9]+)_(.*)$)', $basefile, $matches)) { 174 | return $matches[1]; 175 | } else { 176 | throw new \RuntimeException(sprintf("No revision found in file '%s'.", $basefile)); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "2208544f70bfdfd04c4975112e3a4570", 8 | "packages": [ 9 | { 10 | "name": "doctrine/annotations", 11 | "version": "v1.2.3", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/doctrine/annotations.git", 15 | "reference": "eeda578cbe24a170331a1cfdf78be723412df7a4" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/doctrine/annotations/zipball/eeda578cbe24a170331a1cfdf78be723412df7a4", 20 | "reference": "eeda578cbe24a170331a1cfdf78be723412df7a4", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "doctrine/lexer": "1.*", 25 | "php": ">=5.3.2" 26 | }, 27 | "require-dev": { 28 | "doctrine/cache": "1.*", 29 | "phpunit/phpunit": "4.*" 30 | }, 31 | "type": "library", 32 | "extra": { 33 | "branch-alias": { 34 | "dev-master": "1.3.x-dev" 35 | } 36 | }, 37 | "autoload": { 38 | "psr-0": { 39 | "Doctrine\\Common\\Annotations\\": "lib/" 40 | } 41 | }, 42 | "notification-url": "https://packagist.org/downloads/", 43 | "license": [ 44 | "MIT" 45 | ], 46 | "authors": [ 47 | { 48 | "name": "Roman Borschel", 49 | "email": "roman@code-factory.org" 50 | }, 51 | { 52 | "name": "Benjamin Eberlei", 53 | "email": "kontakt@beberlei.de" 54 | }, 55 | { 56 | "name": "Guilherme Blanco", 57 | "email": "guilhermeblanco@gmail.com" 58 | }, 59 | { 60 | "name": "Jonathan Wage", 61 | "email": "jonwage@gmail.com" 62 | }, 63 | { 64 | "name": "Johannes Schmitt", 65 | "email": "schmittjoh@gmail.com" 66 | } 67 | ], 68 | "description": "Docblock Annotations Parser", 69 | "homepage": "http://www.doctrine-project.org", 70 | "keywords": [ 71 | "annotations", 72 | "docblock", 73 | "parser" 74 | ], 75 | "time": "2014-12-20 20:49:38" 76 | }, 77 | { 78 | "name": "doctrine/cache", 79 | "version": "v1.4.0", 80 | "source": { 81 | "type": "git", 82 | "url": "https://github.com/doctrine/cache.git", 83 | "reference": "2346085d2b027b233ae1d5de59b07440b9f288c8" 84 | }, 85 | "dist": { 86 | "type": "zip", 87 | "url": "https://api.github.com/repos/doctrine/cache/zipball/2346085d2b027b233ae1d5de59b07440b9f288c8", 88 | "reference": "2346085d2b027b233ae1d5de59b07440b9f288c8", 89 | "shasum": "" 90 | }, 91 | "require": { 92 | "php": ">=5.3.2" 93 | }, 94 | "conflict": { 95 | "doctrine/common": ">2.2,<2.4" 96 | }, 97 | "require-dev": { 98 | "phpunit/phpunit": ">=3.7", 99 | "predis/predis": "~0.8", 100 | "satooshi/php-coveralls": "~0.6" 101 | }, 102 | "type": "library", 103 | "extra": { 104 | "branch-alias": { 105 | "dev-master": "1.4.x-dev" 106 | } 107 | }, 108 | "autoload": { 109 | "psr-0": { 110 | "Doctrine\\Common\\Cache\\": "lib/" 111 | } 112 | }, 113 | "notification-url": "https://packagist.org/downloads/", 114 | "license": [ 115 | "MIT" 116 | ], 117 | "authors": [ 118 | { 119 | "name": "Roman Borschel", 120 | "email": "roman@code-factory.org" 121 | }, 122 | { 123 | "name": "Benjamin Eberlei", 124 | "email": "kontakt@beberlei.de" 125 | }, 126 | { 127 | "name": "Guilherme Blanco", 128 | "email": "guilhermeblanco@gmail.com" 129 | }, 130 | { 131 | "name": "Jonathan Wage", 132 | "email": "jonwage@gmail.com" 133 | }, 134 | { 135 | "name": "Johannes Schmitt", 136 | "email": "schmittjoh@gmail.com" 137 | } 138 | ], 139 | "description": "Caching library offering an object-oriented API for many cache backends", 140 | "homepage": "http://www.doctrine-project.org", 141 | "keywords": [ 142 | "cache", 143 | "caching" 144 | ], 145 | "time": "2015-01-15 20:38:55" 146 | }, 147 | { 148 | "name": "doctrine/collections", 149 | "version": "v1.2", 150 | "source": { 151 | "type": "git", 152 | "url": "https://github.com/doctrine/collections.git", 153 | "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2" 154 | }, 155 | "dist": { 156 | "type": "zip", 157 | "url": "https://api.github.com/repos/doctrine/collections/zipball/b99c5c46c87126201899afe88ec490a25eedd6a2", 158 | "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2", 159 | "shasum": "" 160 | }, 161 | "require": { 162 | "php": ">=5.3.2" 163 | }, 164 | "type": "library", 165 | "extra": { 166 | "branch-alias": { 167 | "dev-master": "1.2.x-dev" 168 | } 169 | }, 170 | "autoload": { 171 | "psr-0": { 172 | "Doctrine\\Common\\Collections\\": "lib/" 173 | } 174 | }, 175 | "notification-url": "https://packagist.org/downloads/", 176 | "license": [ 177 | "MIT" 178 | ], 179 | "authors": [ 180 | { 181 | "name": "Jonathan Wage", 182 | "email": "jonwage@gmail.com", 183 | "homepage": "http://www.jwage.com/", 184 | "role": "Creator" 185 | }, 186 | { 187 | "name": "Guilherme Blanco", 188 | "email": "guilhermeblanco@gmail.com", 189 | "homepage": "http://www.instaclick.com" 190 | }, 191 | { 192 | "name": "Roman Borschel", 193 | "email": "roman@code-factory.org" 194 | }, 195 | { 196 | "name": "Benjamin Eberlei", 197 | "email": "kontakt@beberlei.de" 198 | }, 199 | { 200 | "name": "Johannes Schmitt", 201 | "email": "schmittjoh@gmail.com", 202 | "homepage": "https://github.com/schmittjoh", 203 | "role": "Developer of wrapped JMSSerializerBundle" 204 | } 205 | ], 206 | "description": "Collections Abstraction library", 207 | "homepage": "http://www.doctrine-project.org", 208 | "keywords": [ 209 | "array", 210 | "collections", 211 | "iterator" 212 | ], 213 | "time": "2014-02-03 23:07:43" 214 | }, 215 | { 216 | "name": "doctrine/common", 217 | "version": "v2.4.2", 218 | "source": { 219 | "type": "git", 220 | "url": "https://github.com/doctrine/common.git", 221 | "reference": "5db6ab40e4c531f14dad4ca96a394dfce5d4255b" 222 | }, 223 | "dist": { 224 | "type": "zip", 225 | "url": "https://api.github.com/repos/doctrine/common/zipball/5db6ab40e4c531f14dad4ca96a394dfce5d4255b", 226 | "reference": "5db6ab40e4c531f14dad4ca96a394dfce5d4255b", 227 | "shasum": "" 228 | }, 229 | "require": { 230 | "doctrine/annotations": "1.*", 231 | "doctrine/cache": "1.*", 232 | "doctrine/collections": "1.*", 233 | "doctrine/inflector": "1.*", 234 | "doctrine/lexer": "1.*", 235 | "php": ">=5.3.2" 236 | }, 237 | "require-dev": { 238 | "phpunit/phpunit": "~3.7" 239 | }, 240 | "type": "library", 241 | "extra": { 242 | "branch-alias": { 243 | "dev-master": "2.4.x-dev" 244 | } 245 | }, 246 | "autoload": { 247 | "psr-0": { 248 | "Doctrine\\Common\\": "lib/" 249 | } 250 | }, 251 | "notification-url": "https://packagist.org/downloads/", 252 | "license": [ 253 | "MIT" 254 | ], 255 | "authors": [ 256 | { 257 | "name": "Jonathan Wage", 258 | "email": "jonwage@gmail.com", 259 | "homepage": "http://www.jwage.com/", 260 | "role": "Creator" 261 | }, 262 | { 263 | "name": "Guilherme Blanco", 264 | "email": "guilhermeblanco@gmail.com", 265 | "homepage": "http://www.instaclick.com" 266 | }, 267 | { 268 | "name": "Roman Borschel", 269 | "email": "roman@code-factory.org" 270 | }, 271 | { 272 | "name": "Benjamin Eberlei", 273 | "email": "kontakt@beberlei.de" 274 | }, 275 | { 276 | "name": "Johannes Schmitt", 277 | "email": "schmittjoh@gmail.com", 278 | "homepage": "https://github.com/schmittjoh", 279 | "role": "Developer of wrapped JMSSerializerBundle" 280 | } 281 | ], 282 | "description": "Common Library for Doctrine projects", 283 | "homepage": "http://www.doctrine-project.org", 284 | "keywords": [ 285 | "annotations", 286 | "collections", 287 | "eventmanager", 288 | "persistence", 289 | "spl" 290 | ], 291 | "time": "2014-05-21 19:28:51" 292 | }, 293 | { 294 | "name": "doctrine/dbal", 295 | "version": "v2.5.1", 296 | "source": { 297 | "type": "git", 298 | "url": "https://github.com/doctrine/dbal.git", 299 | "reference": "628c2256b646ae2417d44e063bce8aec5199d48d" 300 | }, 301 | "dist": { 302 | "type": "zip", 303 | "url": "https://api.github.com/repos/doctrine/dbal/zipball/628c2256b646ae2417d44e063bce8aec5199d48d", 304 | "reference": "628c2256b646ae2417d44e063bce8aec5199d48d", 305 | "shasum": "" 306 | }, 307 | "require": { 308 | "doctrine/common": ">=2.4,<2.6-dev", 309 | "php": ">=5.3.2" 310 | }, 311 | "require-dev": { 312 | "phpunit/phpunit": "4.*", 313 | "symfony/console": "2.*" 314 | }, 315 | "suggest": { 316 | "symfony/console": "For helpful console commands such as SQL execution and import of files." 317 | }, 318 | "bin": [ 319 | "bin/doctrine-dbal" 320 | ], 321 | "type": "library", 322 | "extra": { 323 | "branch-alias": { 324 | "dev-master": "2.5.x-dev" 325 | } 326 | }, 327 | "autoload": { 328 | "psr-0": { 329 | "Doctrine\\DBAL\\": "lib/" 330 | } 331 | }, 332 | "notification-url": "https://packagist.org/downloads/", 333 | "license": [ 334 | "MIT" 335 | ], 336 | "authors": [ 337 | { 338 | "name": "Roman Borschel", 339 | "email": "roman@code-factory.org" 340 | }, 341 | { 342 | "name": "Benjamin Eberlei", 343 | "email": "kontakt@beberlei.de" 344 | }, 345 | { 346 | "name": "Guilherme Blanco", 347 | "email": "guilhermeblanco@gmail.com" 348 | }, 349 | { 350 | "name": "Jonathan Wage", 351 | "email": "jonwage@gmail.com" 352 | } 353 | ], 354 | "description": "Database Abstraction Layer", 355 | "homepage": "http://www.doctrine-project.org", 356 | "keywords": [ 357 | "database", 358 | "dbal", 359 | "persistence", 360 | "queryobject" 361 | ], 362 | "time": "2015-01-12 21:52:47" 363 | }, 364 | { 365 | "name": "doctrine/inflector", 366 | "version": "v1.0.1", 367 | "source": { 368 | "type": "git", 369 | "url": "https://github.com/doctrine/inflector.git", 370 | "reference": "0bcb2e79d8571787f18b7eb036ed3d004908e604" 371 | }, 372 | "dist": { 373 | "type": "zip", 374 | "url": "https://api.github.com/repos/doctrine/inflector/zipball/0bcb2e79d8571787f18b7eb036ed3d004908e604", 375 | "reference": "0bcb2e79d8571787f18b7eb036ed3d004908e604", 376 | "shasum": "" 377 | }, 378 | "require": { 379 | "php": ">=5.3.2" 380 | }, 381 | "require-dev": { 382 | "phpunit/phpunit": "4.*" 383 | }, 384 | "type": "library", 385 | "extra": { 386 | "branch-alias": { 387 | "dev-master": "1.0.x-dev" 388 | } 389 | }, 390 | "autoload": { 391 | "psr-0": { 392 | "Doctrine\\Common\\Inflector\\": "lib/" 393 | } 394 | }, 395 | "notification-url": "https://packagist.org/downloads/", 396 | "license": [ 397 | "MIT" 398 | ], 399 | "authors": [ 400 | { 401 | "name": "Roman Borschel", 402 | "email": "roman@code-factory.org" 403 | }, 404 | { 405 | "name": "Benjamin Eberlei", 406 | "email": "kontakt@beberlei.de" 407 | }, 408 | { 409 | "name": "Guilherme Blanco", 410 | "email": "guilhermeblanco@gmail.com" 411 | }, 412 | { 413 | "name": "Jonathan Wage", 414 | "email": "jonwage@gmail.com" 415 | }, 416 | { 417 | "name": "Johannes Schmitt", 418 | "email": "schmittjoh@gmail.com" 419 | } 420 | ], 421 | "description": "Common String Manipulations with regard to casing and singular/plural rules.", 422 | "homepage": "http://www.doctrine-project.org", 423 | "keywords": [ 424 | "inflection", 425 | "pluralize", 426 | "singularize", 427 | "string" 428 | ], 429 | "time": "2014-12-20 21:24:13" 430 | }, 431 | { 432 | "name": "doctrine/lexer", 433 | "version": "v1.0.1", 434 | "source": { 435 | "type": "git", 436 | "url": "https://github.com/doctrine/lexer.git", 437 | "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" 438 | }, 439 | "dist": { 440 | "type": "zip", 441 | "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", 442 | "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", 443 | "shasum": "" 444 | }, 445 | "require": { 446 | "php": ">=5.3.2" 447 | }, 448 | "type": "library", 449 | "extra": { 450 | "branch-alias": { 451 | "dev-master": "1.0.x-dev" 452 | } 453 | }, 454 | "autoload": { 455 | "psr-0": { 456 | "Doctrine\\Common\\Lexer\\": "lib/" 457 | } 458 | }, 459 | "notification-url": "https://packagist.org/downloads/", 460 | "license": [ 461 | "MIT" 462 | ], 463 | "authors": [ 464 | { 465 | "name": "Roman Borschel", 466 | "email": "roman@code-factory.org" 467 | }, 468 | { 469 | "name": "Guilherme Blanco", 470 | "email": "guilhermeblanco@gmail.com" 471 | }, 472 | { 473 | "name": "Johannes Schmitt", 474 | "email": "schmittjoh@gmail.com" 475 | } 476 | ], 477 | "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", 478 | "homepage": "http://www.doctrine-project.org", 479 | "keywords": [ 480 | "lexer", 481 | "parser" 482 | ], 483 | "time": "2014-09-09 13:34:57" 484 | } 485 | ], 486 | "packages-dev": [], 487 | "aliases": [], 488 | "minimum-stability": "stable", 489 | "stability-flags": [], 490 | "prefer-stable": false, 491 | "prefer-lowest": false, 492 | "platform": { 493 | "php": "~5.4" 494 | }, 495 | "platform-dev": [] 496 | } 497 | --------------------------------------------------------------------------------