From 8256ab5dd901904dfa8f78ebc3f3ed08a969226d Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 3 Mar 2021 23:38:52 +0300 Subject: [PATCH] wip: initial for db_migrations --- classes/db/migrations.php | 140 ++++++++++++++++++++++++++++++++++++++ classes/pluginhost.php | 7 +- 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 classes/db/migrations.php diff --git a/classes/db/migrations.php b/classes/db/migrations.php new file mode 100644 index 000000000..d1eee61d3 --- /dev/null +++ b/classes/db/migrations.php @@ -0,0 +1,140 @@ +get_plugin_dir($plugin); + $this->initialize($plugin_dir . "/${schema_suffix}", + strtolower("ttrss_migrations_plugin_" . get_class($plugin))); + } + + function initialize(string $root_path, string $migrations_table) { + $this->base_path = "$root_path/" . Config::get(Config::DB_TYPE); + $this->migrations_path = $this->base_path . "/migrations"; + + $this->migrations_table = $migrations_table; + } + + function __construct() { + $this->pdo = Db::pdo(); + } + + private function set_version(int $version) { + $sth = $this->pdo->query("SELECT * FROM {$this->migrations_table}"); + + if ($res = $sth->fetch()) { + $sth = $this->pdo->prepare("UPDATE {$this->migrations_table} SET schema_version = ?"); + } else { + $sth = $this->pdo->prepare("INSERT INTO {$this->migrations_table} (schema_version) VALUES (?)"); + } + + $sth->execute([$version]); + + $this->cached_version = $version; + } + + private function get_version() : int { + if (isset($this->cached_version)) + return $this->cached_version; + + try { + $sth = $this->pdo->query("SELECT * FROM {$this->migrations_table}"); + + if ($res = $sth->fetch()) { + return (int) $res['schema_version']; + } else { + return -1; + } + } catch (PDOException $e) { + $this->create_migrations_table(); + + return -1; + } + } + + private function create_migrations_table() { + $this->pdo->query("CREATE TABLE IF NOT EXISTS {$this->migrations_table} (schema_version integer not null)"); + } + + private function migrate_to(int $version) { + try { + $this->pdo->beginTransaction(); + + foreach ($this->get_lines($version) as $line) { + $this->pdo->query($line); + } + + $this->set_version($version); + + $this->pdo->commit(); + } catch (PDOException $e) { + try { + $this->pdo->rollback(); + } catch (PDOException $ie) { + // + } + throw $e; + } + } + + private function get_max_version() : int { + if (isset($this->cached_max_version)) + return $this->cached_max_version; + + $migrations = glob("{$this->migrations_path}/*.sql"); + + if (count($migrations) > 0) { + natsort($migrations); + + $this->cached_max_version = (int) basename(array_pop($migrations), ".sql"); + + } else { + $this->cached_max_version = 0; + } + + return $this->cached_max_version; + } + + function migrate() : bool { + + for ($i = $this->get_version() + 1; $i <= $this->get_max_version(); $i++) + try { + $this->migrate_to($i); + } catch (PDOException $e) { + user_error("Failed applying migration $i on table {$this->migrations_table}: " . $e->getMessage(), E_USER_WARNING); + //throw $e; + } + + return $this->get_version() == $this->get_max_version(); + } + + private function get_lines(int $version) : array { + if ($version > 0) + $filename = "{$this->migrations_path}/${version}.sql"; + else + $filename = "{$this->base_path}/{$this->base_filename}"; + + if (file_exists($filename)) { + $lines = array_filter(preg_split("/[\r\n]/", file_get_contents($filename)), + function ($line) { + return strlen(trim($line)) > 0 && strpos($line, "--") !== 0; + }); + + return array_filter(explode(";", implode("", $lines)), function ($line) { + return strlen(trim($line)) > 0; + }); + + } else { + user_error(E_USER_ERROR, "[migrations] requested schema file ${filename} not found."); + return []; + } + } +} diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 746b780e4..366e2b2d3 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -624,9 +624,14 @@ class PluginHost { } } - function is_local(Plugin $plugin) { + function get_plugin_dir(Plugin $plugin) { $ref = new ReflectionClass(get_class($plugin)); + return dirname($ref->getFileName()); + } + // TODO: use get_plugin_dir() + function is_local(Plugin $plugin) { + $ref = new ReflectionClass(get_class($plugin)); return basename(dirname(dirname($ref->getFileName()))) == "plugins.local"; } }