diff --git a/CHANGELOG.md b/CHANGELOG.md index f5a3b6165..70a7289b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Exclamation symbols (:exclamation:) note something of importance e.g. breaking c ## [Unreleased] ### Added +- Database migrations using [Phinx]. ### Changed ### Deprecated ### Removed @@ -280,6 +281,7 @@ Exclamation symbols (:exclamation:) note something of importance e.g. breaking c [0.45.0-bc-up-download-directory]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#up-download-directory [0.44.0-bc-update-content-type]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#update-getupdatecontent [example-bot]: https://github.com/php-telegram-bot/example-bot +[Phinx]: https://github.com/cakephp/phinx [Unreleased]: https://github.com/php-telegram-bot/core/compare/master...develop [0.57.0]: https://github.com/php-telegram-bot/core/compare/0.56.0...0.57.0 diff --git a/composer.json b/composer.json index d033b62b4..d047af8a7 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,8 @@ "ext-json": "*", "ext-mbstring": "*", "monolog/monolog": "^1.24", - "guzzlehttp/guzzle": "^6.3" + "guzzlehttp/guzzle": "^6.3", + "robmorgan/phinx": "^0.9|^0.10" }, "require-dev": { "phpunit/phpunit": "^4.8|^5.7|^6.5|^7.5|^8.1", diff --git a/composer.lock b/composer.lock index cd26f42bc..aeefe9113 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b3ac8abe31dc69491d76f5b4bb65ca26", + "content-hash": "603541a5836829dc34f17fed59fb1c21", "packages": [ { "name": "guzzlehttp/guzzle", @@ -403,6 +403,495 @@ ], "description": "A polyfill for getallheaders.", "time": "2016-02-11T07:05:27+00:00" + }, + { + "name": "robmorgan/phinx", + "version": "0.9.2", + "source": { + "type": "git", + "url": "https://github.com/cakephp/phinx.git", + "reference": "e1698319ad55157c233b658c08f7a10617e797ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/e1698319ad55157c233b658c08f7a10617e797ca", + "reference": "e1698319ad55157c233b658c08f7a10617e797ca", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "symfony/config": "^2.8|^3.0|^4.0", + "symfony/console": "^2.8|^3.0|^4.0", + "symfony/yaml": "^2.8|^3.0|^4.0" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "^3.0", + "phpunit/phpunit": "^4.8.35|^5.7|^6.5" + }, + "bin": [ + "bin/phinx" + ], + "type": "library", + "autoload": { + "psr-4": { + "Phinx\\": "src/Phinx" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "http://shadowhand.me", + "role": "Developer" + }, + { + "name": "Rob Morgan", + "email": "robbym@gmail.com", + "homepage": "https://robmorgan.id.au", + "role": "Lead Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Developer" + }, + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/phinx/graphs/contributors" + } + ], + "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.", + "homepage": "https://phinx.org", + "keywords": [ + "database", + "database migrations", + "db", + "migrations", + "phinx" + ], + "time": "2017-12-23T06:48:51+00:00" + }, + { + "name": "symfony/config", + "version": "v3.4.28", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "177a276c01575253c95cefe0866e3d1b57637fe0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/177a276c01575253c95cefe0866e3d1b57637fe0", + "reference": "177a276c01575253c95cefe0866e3d1b57637fe0", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "https://symfony.com", + "time": "2019-02-23T15:06:07+00:00" + }, + { + "name": "symfony/console", + "version": "v3.4.28", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "8e1d1e406dd31727fa70cd5a99cda202e9d6a5c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/8e1d1e406dd31727fa70cd5a99cda202e9d6a5c6", + "reference": "8e1d1e406dd31727fa70cd5a99cda202e9d6a5c6", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2019-05-09T08:42:51+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.4.28", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "671fc55bd14800668b1d0a3708c3714940e30a8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/671fc55bd14800668b1d0a3708c3714940e30a8c", + "reference": "671fc55bd14800668b1d0a3708c3714940e30a8c", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2019-05-18T13:32:47+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v3.4.28", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "acf99758b1df8e9295e6b85aa69f294565c9fedb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/acf99758b1df8e9295e6b85aa69f294565c9fedb", + "reference": "acf99758b1df8e9295e6b85aa69f294565c9fedb", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2019-02-04T21:34:32+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "82ebae02209c21113908c229e9883c419720738a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", + "reference": "82ebae02209c21113908c229e9883c419720738a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "backendtea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2019-02-06T07:57:58+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2019-02-06T07:57:58+00:00" + }, + { + "name": "symfony/yaml", + "version": "v3.4.28", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/212a27b731e5bfb735679d1ffaac82bd6a1dc996", + "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2019-03-25T07:48:46+00:00" } ], "packages-dev": [ @@ -1469,123 +1958,6 @@ ], "time": "2019-04-10T23:49:02+00:00" }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.11.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "82ebae02209c21113908c229e9883c419720738a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", - "reference": "82ebae02209c21113908c229e9883c419720738a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, - { - "name": "Gert de Pagter", - "email": "backendtea@gmail.com" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2019-02-06T07:57:58+00:00" - }, - { - "name": "symfony/yaml", - "version": "v3.4.28", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/212a27b731e5bfb735679d1ffaac82bd6a1dc996", - "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2019-03-25T07:48:46+00:00" - }, { "name": "webmozart/assert", "version": "1.4.0", diff --git a/phinx.php b/phinx.php new file mode 100644 index 000000000..e16fa7e21 --- /dev/null +++ b/phinx.php @@ -0,0 +1,7 @@ + [ + 'migrations' => '%%PHINX_CONFIG_DIR%%/utils/db-migrations', + ], +]; diff --git a/src/DB.php b/src/DB.php index d3330f0b5..132091cc1 100644 --- a/src/DB.php +++ b/src/DB.php @@ -25,6 +25,10 @@ use Longman\TelegramBot\Exception\TelegramException; use PDO; use PDOException; +use Phinx\Config\Config; +use Phinx\Migration\Manager; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Output\ConsoleOutput; class DB { @@ -1299,4 +1303,50 @@ public static function update($table, array $fields_values, array $where_fields_ throw new TelegramException($e->getMessage()); } } + + /** + * Run DB migrations using Phinx. + * + * @return bool + */ + public static function runMigrations() + { + if (!self::isDbConnected()) { + return false; + } + + // Need to require autoloader to get Phinx classes loaded. + require_once __DIR__ . '/../vendor/autoload.php'; + + $config = new Config([ + 'paths' => [ + 'migrations' => __DIR__ . '/../utils/db-migrations', + ], + 'environments' => [ + 'default_migration_table' => self::$table_prefix . 'phinx_migrations', + 'default_database' => 'core', + 'core' => [ + 'connection' => self::getPdo(), + 'table_prefix' => self::$table_prefix, + 'name' => self::$mysql_credentials['database'], + ], + ], + ]); + + $manager = new Manager($config, $input = new ArgvInput(), $output = new ConsoleOutput()); + + $output->writeln(date('Y-m-d H:i:s') . ' - Migration started.'); + + switch ($input->getFirstArgument()) { + case 'rollback': + $manager->rollback('core'); + break; + default: + $manager->migrate('core'); + } + + $output->writeln(date('Y-m-d H:i:s') . ' - Migration completed.'); + + return true; + } } diff --git a/utils/db-migrations/README.md b/utils/db-migrations/README.md new file mode 100644 index 000000000..fa3c06624 --- /dev/null +++ b/utils/db-migrations/README.md @@ -0,0 +1,59 @@ +# Database migrations using [Phinx] + +## For users + +### Run the migrations + +You'll need to create a new file where the database connection is initialised, to allow the migration to do its thing. + +Minimal example `db-migration.php` in the root of your project: +```php + 'localhost', + 'port' => 3306, // optional + 'user' => 'dbuser', + 'password' => 'dbpass', + 'database' => 'dbname', +]; + +try { + // Create Telegram API object (with dummy API key) + $telegram = new Telegram('1:a'); + + // Initialise the database connection. + $telegram->enableMySql($mysql_credentials); + + // Run the migration! + DB::runMigrations(); +} catch (Exception $e) { + echo $e->getMessage(); +} +``` + +This script can now be called to perform migrations and rollbacks: +```bash +# Migrate +$ php db-migration.php + +# Rollback +$ php db-migration.php rollback +``` + +## For developers + +### Create new migration + +- `$ vendor/bin/phinx create MyMigrationName` +- Edit new migration template. +- User can now call DB migration script. + +[Phinx]: https://github.com/cakephp/phinx