$restore_finished array where jobs can mark as finished * @property string $uploads_folder uploads folder within the restore dir * @property string|null $service_name * @property int|null $job_id * @property string $old_url the old URL to migrate from * @property string $new_url the new URL to migrate to * * @psalm-import-type DbPos from \Inpsyde\Restore\Api\Module\Database\ImportFileInterface * @psalm-import-type DecompressionState from \Inpsyde\Restore\Api\Module\Decompress\StateUpdater */ class Registry { /** * @var string */ private $filePath; /** * Internal registry. * * @var mixed[] */ private $registry = []; /** * @param string $path The Path to the save file * * @throws InvalidArgumentException */ public function __construct(string $path) { $sanitizedPath = SanitizePath::sanitize($path); if ($sanitizedPath !== $path) { throw new InvalidArgumentException( 'Given Path seems corrupted when construct the registry instance.' ); } $this->filePath = $path; $this->init(); } /** * Add/Update a value in the registry. * * @param string $key The registry key * @param mixed $value Value associated with the key * * @throws Exception In case the registry cannot be saved */ public function __set(string $key, $value): void // phpcs:ignore { $this->registry[$key] = $value; $this->save(); } /** * Check if value is set in registry. * * @param string $name The registry key * * @return bool */ public function __isset(string $name): bool { return \array_key_exists($name, $this->registry); } /** * Perform the basic tasks to make the registry work properly. * * @throws FileSystemException */ public function init(): void { if (file_exists($this->filePath)) { $data = file_get_contents($this->filePath) ?: ''; $unserializedData = unserialize($data); $this->registry = \is_array($unserializedData) ? $unserializedData : []; return; } if (!\defined('FS_CHMOD_DIR')) { \define('FS_CHMOD_DIR', 0775); } if (!file_exists(\dirname($this->filePath))) { // Attempt to create the directories if not exists. mkdir(\dirname($this->filePath), FS_CHMOD_DIR, true); } // If file doesn't exists let's try to create it. $handle = fopen($this->filePath, 'a+'); // If file cannot be created log it for support. if (!$handle) { throw new FileSystemException( 'Cannot possible to create the registry file. Restore will not work properly.' ); } // Release the resource. fclose($handle); } /** * Save the registry to the save file. * * @throws FileSystemException In case the registry cannot be saved */ public function save(): void { $data = serialize($this->registry); // phpcs:ignore // When writing get an exclusive lock on the file to avoid conflicts if (file_put_contents($this->filePath, $data, LOCK_EX) === false) { // phpcs:ignore throw new FileSystemException( sprintf( 'Could not write Registry file to %s', $this->filePath ) ); } } /** * Get a value from the registry. * * @param string $key The registry key * * @return mixed */ public function &__get(string $key) // phpcs:ignore { if (!\array_key_exists($key, $this->registry)) { $this->registry[$key] = null; } return $this->registry[$key]; } /** * Check if registry has a specific key set. * * @param string $key Name of registry key */ public function has(string $key): bool { return $this->__isset($key); } /** * Remove an entry from the registry. * * @param string $key The registry key * * @throws Exception In case the registry cannot be saved */ public function delete(string $key): void { unset($this->registry[$key]); $this->save(); } /** * Add file to blacklist. * * Helper function to add file names to a blacklist for file restore * * @param string $file_name The file name to exclude * * @throws Exception In case the registry cannot be saved */ public function add_to_blacklist(string $file_name): void { if ($this->extra_files === null) { $this->extra_files = []; } $this->extra_files[] = $file_name; $this->save(); } /** * Store the finished jobs. * * @param string $job_name The job to store * * @throws Exception In case the registry cannot be saved */ public function finish_job(string $job_name): void { if ( $this->restore_finished === null || !\is_array($this->restore_finished) ) { $this->restore_finished = []; } $this->restore_finished[$job_name] = 'finished'; $this->save(); } /** * Helper function to add dirs to restore_list. * * @param string $dir The dir to store in the registry * * @throws Exception In case the registry cannot be saved */ public function add_to_restore_list(string $dir): void { if ($this->restore_list === null || !\is_array($this->restore_list)) { $this->restore_list = []; } $this->restore_list[] = $dir; $this->save(); } /** * Helper function to remove first element in restore_list. * * @throws Exception In case the registry cannot be saved * * @return string The directory to restore */ public function next_dir_in_restore_list(): string { if (empty($this->restore_list)) { return ''; } $current = array_shift($this->restore_list); $this->save(); return $current; } /** * Update the migration progress. * * @param int $percent The progress percentage * * @throws Exception In case the registry cannot be saved */ public function update_progress(int $percent): void { $this->migration_progress = $percent; $this->save(); } /** * Reset registry to start a new restore process. * * A hard and soft reset is possible. A hard one will delete the complete registry and the * app will start from scratch. A soft reset will delete only information regarding the last * restore. I.e. information about language translation, etc. are kept b/c this does not * influence the restore itself. * * @throws Exception In case the registry cannot be saved * * @return $this Registry for concatenation */ public function reset_registry(): self { copy($this->filePath, "{$this->filePath}.bkp"); $this->registry = []; $this->save(); return $this; } /** * Is Restore Finished? * * @return bool True on success, false on error */ public function is_restore_finished(): bool { if (!($this->restore_strategy !== null && $this->restore_finished !== null)) { return false; } $jobs = 1; if ($this->restore_strategy === 'complete restore') { $jobs = 2; } return $jobs === \count($this->restore_finished); } }