From 510377d2cb4b12d1a421e8a88bd7edb86f223451 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 12 Jan 2016 19:50:48 +0100 Subject: Introduce the Updater class which * contains methods designed to be run once. * is able to upgrade the datastore or the configuration. * is based on methods names, stored in a text file with ';' separator (updates.txt). * begins with existing function 'mergeDeprecatedConfigFile()' (options.php). --- application/Updater.php | 228 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 application/Updater.php (limited to 'application/Updater.php') diff --git a/application/Updater.php b/application/Updater.php new file mode 100644 index 00000000..20ae0c4d --- /dev/null +++ b/application/Updater.php @@ -0,0 +1,228 @@ +doneUpdates = $doneUpdates; + $this->config = $config; + $this->linkDB = $linkDB; + $this->isLoggedIn = $isLoggedIn; + + // Retrieve all update methods. + $class = new ReflectionClass($this); + $this->methods = $class->getMethods(); + } + + /** + * Run all new updates. + * Update methods have to start with 'updateMethod' and return true (on success). + * + * @return array An array containing ran updates. + * + * @throws UpdaterException If something went wrong. + */ + public function update() + { + $updatesRan = array(); + + // If the user isn't logged in, exit without updating. + if ($this->isLoggedIn !== true) { + return $updatesRan; + } + + if ($this->methods == null) { + throw new UpdaterException('Couldn\'t retrieve Updater class methods.'); + } + + foreach ($this->methods as $method) { + // Not an update method or already done, pass. + if (! startsWith($method->getName(), 'updateMethod') + || in_array($method->getName(), $this->doneUpdates) + ) { + continue; + } + + try { + $method->setAccessible(true); + $res = $method->invoke($this); + // Update method must return true to be considered processed. + if ($res === true) { + $updatesRan[] = $method->getName(); + } + } catch (Exception $e) { + throw new UpdaterException($method, $e); + } + } + + $this->doneUpdates = array_merge($this->doneUpdates, $updatesRan); + + return $updatesRan; + } + + /** + * @return array Updates methods already processed. + */ + public function getDoneUpdates() + { + return $this->doneUpdates; + } + + /** + * Move deprecated options.php to config.php. + * + * Milestone 0.9 (old versioning) - shaarli/Shaarli#41: + * options.php is not supported anymore. + */ + public function updateMethodMergeDeprecatedConfigFile() + { + $config_file = $this->config['config']['CONFIG_FILE']; + + if (is_file($this->config['config']['DATADIR'].'/options.php')) { + include $this->config['config']['DATADIR'].'/options.php'; + + // Load GLOBALS into config + foreach ($GLOBALS as $key => $value) { + $this->config[$key] = $value; + } + $this->config['config']['CONFIG_FILE'] = $config_file; + writeConfig($this->config, $this->isLoggedIn); + + unlink($this->config['config']['DATADIR'].'/options.php'); + } + + return true; + } +} + +/** + * Class UpdaterException. + */ +class UpdaterException extends Exception +{ + /** + * @var string Method where the error occurred. + */ + protected $method; + + /** + * @var Exception The parent exception. + */ + protected $previous; + + /** + * Constructor. + * + * @param string $message Force the error message if set. + * @param string $method Method where the error occurred. + * @param Exception|bool $previous Parent exception. + */ + public function __construct($message = '', $method = '', $previous = false) + { + $this->method = $method; + $this->previous = $previous; + $this->message = $this->buildMessage($message); + } + + /** + * Build the exception error message. + * + * @param string $message Optional given error message. + * + * @return string The built error message. + */ + private function buildMessage($message) + { + $out = ''; + if (! empty($message)) { + $out .= $message . PHP_EOL; + } + + if (! empty($this->method)) { + $out .= 'An error occurred while running the update '. $this->method . PHP_EOL; + } + + if (! empty($this->previous)) { + $out .= ' '. $this->previous->getMessage(); + } + + return $out; + } +} + + +/** + * Read the updates file, and return already done updates. + * + * @param string $updatesFilepath Updates file path. + * + * @return array Already done update methods. + */ +function read_updates_file($updatesFilepath) +{ + if (! empty($updatesFilepath) && is_file($updatesFilepath)) { + $content = file_get_contents($updatesFilepath); + if (! empty($content)) { + return explode(';', $content); + } + } + return array(); +} + +/** + * Write updates file. + * + * @param string $updatesFilepath Updates file path. + * @param array $updates Updates array to write. + * + * @throws Exception Couldn't write version number. + */ +function write_updates_file($updatesFilepath, $updates) +{ + if (empty($updatesFilepath)) { + throw new Exception('Updates file path is not set, can\'t write updates.'); + } + + $res = file_put_contents($updatesFilepath, implode(';', $updates)); + if ($res === false) { + throw new Exception('Unable to write updates in '. $updatesFilepath . '.'); + } +} -- cgit v1.2.3