Skip to content
Snippets Groups Projects
Verified Commit 16880789 authored by Bartek Jaskulski's avatar Bartek Jaskulski
Browse files

Basic clone of doctrine/migrations adapted to WP environment

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 509 additions and 0 deletions
# Path-based git attributes
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
# Ignore all test and documentation with "export-ignore".
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.gitlab-ci.yml export-ignore
/.git export-ignore
/.phpcs-cache export-ignore
/phpcs.xml.dist export-ignore
/phpstan.neon.dist export-ignore
/phpunit.xml.dist export-ignore
/phpunit-unit.xml export-ignore
/phpunit-integration.xml export-ignore
/.idea export-ignore
/.gitlab export-ignore
/tests export-ignore
/tools export-ignore
{
"name": "wpdesk/wp-migrations",
"type": "library",
"authors": [
{
"name": "Bart Jaskulski",
"email": "bartek.jaskulski@wpdesk.net"
}
],
"require": {
"ext-json": "*",
"psr/log": "^1"
},
"autoload": {
"psr-4": {
"WPDesk\\Migrations\\": "src"
}
}
}
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d171d953d0884cc12571bc6f1e6927b1",
"packages": [
{
"name": "psr/log",
"version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/1.1.4"
},
"time": "2021-05-03T11:20:27+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"ext-json": "*"
},
"platform-dev": [],
"plugin-api-version": "2.3.0"
}
<?php declare(strict_types=1);
namespace WPDesk\Migrations;
use Psr\Log\LoggerInterface;
abstract class AbstractMigration {
protected $wpdb;
protected $logger;
public function __construct(\wpdb $wpdb, LoggerInterface $logger){
$this->wpdb = $wpdb;
$this->logger = $logger;
}
abstract public function up(): bool;
public function down(): void {}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations;
use WPDesk\Migrations\Version\Version;
class AvailableMigration {
private $version;
private $migration;
public function __construct(Version $version, AbstractMigration $migration) {
$this->version = $version;
$this->migration = $migration;
}
public function get_version(): Version {
return $this->version;
}
public function get_migration(): AbstractMigration {
return $this->migration;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations;
use WPDesk\Migrations\Finder\MigrationFinder;
use WPDesk\Migrations\Version\MigrationFactory;
use WPDesk\Migrations\Version\Version;
class FilesystemMigrationsRepository implements MigrationsRepository {
private $migrations_loaded = false;
private $migration_directories;
private $migration_finder;
private $version_factory;
private $migrations = [];
public function __construct(
array $migration_directories,
MigrationFinder $migration_finder,
MigrationFactory $version_factory
) {
$this->migration_directories = $migration_directories;
$this->migration_finder = $migration_finder;
$this->version_factory = $version_factory;
}
public function register_migration(string $migration_class_name) {
$migration = $this->version_factory->create_version($migration_class_name);
$version = new Version($migration_class_name);
$this->migrations[(string) $version] = new AvailableMigration($version, $migration);
}
private function register_migrations(array $migrations){
foreach ($migrations as $migration) {
$this->register_migration($migration);
}
}
public function get_migrations(): iterable {
$this->load_migrations();
return $this->migrations;
}
private function load_migrations(): void {
if ($this->migrations_loaded) {
return;
}
$this->migrations_loaded = true;
foreach ($this->migration_directories as $directory) {
$migrations = $this->migration_finder->find_migrations($directory);
$this->register_migrations($migrations);
}
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations\Finder;
use WPDesk\Migrations\Migration;
final class GlobFinder implements MigrationFinder {
/**
* @param string $directory
* @return class-string<Migration>[]
*/
public function find_migrations(string $directory): array {
$dir = realpath($directory);
$files = glob(rtrim($dir, '/') . '/Version*.php');
if ($files === false) {
$files = [];
}
return $this->load_migrations($files);
}
private function load_migrations(array $files): array {
$included_files = [];
foreach ($files as $file) {
require_once $file;
$real_file = realpath($file);
$included_files[] = $real_file;
}
$classes = $this->load_migration_classes($included_files);
$versions = [];
foreach ($classes as $class) {
$versions[] = $class->getName();
}
return $versions;
}
/**
* @param array $included_files
* @return \ReflectionClass[]
* @throws \ReflectionException
*/
private function load_migration_classes(array $included_files): array {
$classes = [];
foreach (get_declared_classes() as $class) {
$r = new \ReflectionClass($class);
if (in_array($r->getFileName(), $included_files, true)) {
$classes[] = $r;
}
}
return $classes;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations\Finder;
use WPDesk\Migrations\Migration;
interface MigrationFinder {
/**
* @param string $directory
* @return class-string<Migration>[]
*/
public function find_migrations(string $directory): array;
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations;
interface Migration {
public function up( \wpdb $wpdb ): bool;
}
<?php
namespace WPDesk\Migrations;
interface MigrationsRepository
{
/** @return iterable<AvailableMigration> */
public function get_migrations(): iterable;
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations;
interface Migrator {
public function migrate(): void;
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations\Version;
class AlphabeticalComparator implements Comparator {
public function compare(Version $a, Version $b): int {
return strcmp((string) $a, (string) $b);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations\Version;
interface Comparator {
public function compare(Version $a, Version $b): int;
}
\ No newline at end of file
<?php
namespace WPDesk\Migrations\Version;
use WPDesk\Migrations\AbstractMigration;
interface MigrationFactory
{
public function create_version(string $migration_class): AbstractMigration;
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations\Version;
final class Version {
private $version;
public function __construct( string $version ) {
$this->version = $version;
}
public function __toString(): string {
return $this->version;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations\Version;
use Psr\Log\LoggerInterface;
use WPDesk\Migrations\AbstractMigration;
class WpdbMigrationFactory implements MigrationFactory
{
protected $wpdb;
protected $logger;
public function __construct(\wpdb $wpdb, LoggerInterface $logger){
$this->wpdb = $wpdb;
$this->logger = $logger;
}
public function create_version(string $migration_class): AbstractMigration {
return new $migration_class(
$this->wpdb,
$this->logger
);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations;
use Psr\Log\LoggerTrait;
class WpdbLogger implements \Psr\Log\LoggerInterface {
use LoggerTrait;
private const DB_LOG = 'omnibus_db_log';
private const MAX_LOG_SIZE = 30;
private $log;
public function __construct() {
$this->log = json_decode( get_option( self::DB_LOG, '[]' ), true );
if ( ! \is_array( $this->log ) ) {
$this->log = [];
}
}
public function log($level, $message, array $context = array()) {
$this->log[] = date( 'Y-m-d G:i:s' ) . sprintf(': %s', $message);
if ( \count( $this->log ) > self::MAX_LOG_SIZE ) {
array_shift( $this->log );
}
update_option( self::DB_LOG, json_encode( $this->log ), false );
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace WPDesk\Migrations;
use Psr\Log\LoggerInterface;
use WPDesk\Migrations\Finder\GlobFinder;
use WPDesk\Migrations\Version\AlphabeticalComparator;
use WPDesk\Migrations\Version\Version;
use WPDesk\Migrations\Version\WpdbMigrationFactory;
class WpdbMigrator implements Migrator {
private const DB_VERSION = 'omnibus_db_version';
/** @var \wpdb */
private $wpdb;
private $migrations_repository;
private $logger;
public static function fromWpdb(\wpdb $wpdb, array $migration_directories): self {
$logger = new WpdbLogger();
return new self(
$wpdb,
new FilesystemMigrationsRepository(
$migration_directories,
new GlobFinder(),
new WpdbMigrationFactory(
$wpdb,
$logger
)
),
$logger
);
}
public function __construct(
\wpdb $wpdb,
MigrationsRepository $migrations_repository,
LoggerInterface $logger
) {
$this->wpdb = $wpdb;
$this->migrations_repository = $migrations_repository;
$this->logger = $logger;
}
private function get_current_version(): Version {
return new Version( get_option( self::DB_VERSION, '' ) );
}
public function migrate(): void {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$this->logger->info( 'DB update start' );
$current_version = $this->get_current_version();
$comparator = new AlphabeticalComparator();
foreach ( $this->migrations_repository->get_migrations() as $migration ) {
if ( $comparator->compare($migration->get_version(), $this->get_current_version()) ) {
$this->logger->info( sprintf('DB update %s:%s', $current_version, $migration->get_version()) );
try {
$migration->get_migration()->up();
$this->logger->info( sprintf('DB update %s:%s -> ', $current_version, $migration->get_version()) . 'OK');
update_option( self::DB_VERSION, (string) $migration->get_version(), true );
} catch (\Throwable $e) {
$error_msg = sprintf('Error while upgrading a database: %s', $this->wpdb->last_error);
$this->logger->error( $error_msg );
trigger_error( $error_msg, E_USER_WARNING );
}
}
}
$this->logger->info( 'DB update finished' );
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment