PluginProbe
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder / 3.6.0
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder v3.6.0
3.6.0 3.5.0 trunk 1.0.10 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.6.1 1.0.7 1.0.8 1.0.9 1.1 1.1.1 1.2 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5 1.5.1 All 110 releases
buttonizer-multifunctional-button / app / Migration / Backup.php

Backup.php in Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder 3.6.0, at app/Migration/Backup.php

185 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 * SOFTWARE LICENSE INFORMATION
4 *
5 * Copyright (c) 2017 Buttonizer, all rights reserved.
6 *
7 * This file is part of Buttonizer
8 *
9 * For detailed information regarding to the licensing of
10 * this software, please review the license.txt or visit:
11 * https://buttonizer.pro/license/
12 */
13
14 namespace Buttonizer\Migration;
15
16 use Buttonizer\Core\PluginConfig;
17
18 # No script kiddies
19 defined('ABSPATH') or die('No script kiddies please!');
20
21 /**
22 * Snapshot of a source plugin's data, taken before anything is touched.
23 *
24 * One option per module (buttonizer_migration_backup_<module>) so a second
25 * acquisition never overwrites the first, and so a single module can be
26 * restored or dropped on its own.
27 *
28 * Follows the same idea as the legacy *_backup_30 snapshots: nothing in the
29 * migration is destructive while this exists.
30 */
31 class Backup
32 {
33 const OPTION_PREFIX = 'buttonizer_migration_backup_';
34
35 /** Marker for options that did not exist when the snapshot was taken. */
36 const MISSING = '__buttonizer_option_missing__';
37
38 /**
39 * Option name holding the backup for a module.
40 */
41 public static function optionName(SourcePlugin $source): string
42 {
43 return self::OPTION_PREFIX . str_replace('-', '_', $source->id());
44 }
45
46 /**
47 * Does a backup already exist?
48 */
49 public static function exists(SourcePlugin $source): bool
50 {
51 return get_option(self::optionName($source), null) !== null;
52 }
53
54 /**
55 * Read the stored backup.
56 *
57 * @return array Empty array when there is none.
58 */
59 public static function get(SourcePlugin $source): array
60 {
61 $backup = get_option(self::optionName($source), []);
62
63 return is_array($backup) ? $backup : [];
64 }
65
66 /**
67 * Snapshot every option belonging to the source plugin.
68 *
69 * The first snapshot wins: re-running the migration must never overwrite
70 * the pre-migration state with post-migration values.
71 *
72 * @param SourcePlugin $source Source plugin descriptor.
73 * @param bool $force Overwrite an existing backup.
74 *
75 * @return bool Whether a snapshot was written.
76 */
77 public static function create(SourcePlugin $source, bool $force = false): bool
78 {
79 if (!$force && self::exists($source)) {
80 return false;
81 }
82
83 return update_option(self::optionName($source), [
84 'created_at' => (new \DateTime('now'))->format(\DateTime::ATOM),
85 'plugin_version' => BUTTONIZER_VERSION,
86 'base_name' => $source->resolveBaseName(),
87 'was_active' => $source->isActive(),
88 'options' => self::snapshot($source->optionsToBackup()),
89
90 // Adopting a connection overwrites Buttonizer's own settings, so
91 // without this half of the snapshot there is no way back to the
92 // state both plugins were in before the migration.
93 'target_options' => self::snapshot(self::targetOptions()),
94 ]);
95 }
96
97 /**
98 * Restore a snapshot.
99 *
100 * Puts every option back exactly as it was, including deleting options
101 * that did not exist before the migration.
102 *
103 * @return bool Whether a backup was found and restored.
104 */
105 public static function restore(SourcePlugin $source): bool
106 {
107 $backup = self::get($source);
108
109 if (empty($backup['options']) || !is_array($backup['options'])) {
110 return false;
111 }
112
113 self::restoreOptions($backup['options']);
114
115 if (!empty($backup['target_options']) && is_array($backup['target_options'])) {
116 self::restoreOptions($backup['target_options']);
117
118 // The token is cached in a transient too, and a stale one would
119 // resurrect a connection the snapshot does not contain.
120 delete_transient(PluginConfig::name() . '_site_connection');
121 }
122
123 return true;
124 }
125
126 /**
127 * Read a set of options, marking the ones that do not exist.
128 *
129 * @param string[] $optionNames Option names.
130 *
131 * @return array<string, mixed>
132 */
133 private static function snapshot(array $optionNames): array
134 {
135 $snapshot = [];
136
137 foreach ($optionNames as $option) {
138 $snapshot[$option] = get_option($option, self::MISSING);
139 }
140
141 return $snapshot;
142 }
143
144 /**
145 * Write a set of options back, deleting the ones that did not exist.
146 *
147 * @param array<string, mixed> $options Snapshotted options.
148 */
149 private static function restoreOptions(array $options): void
150 {
151 foreach ($options as $option => $value) {
152 if ($value === self::MISSING) {
153 delete_option($option);
154 continue;
155 }
156
157 update_option($option, $value);
158 }
159 }
160
161 /**
162 * Buttonizer's own connection options.
163 *
164 * @return string[]
165 */
166 private static function targetOptions(): array
167 {
168 $name = PluginConfig::name();
169
170 return [
171 $name . '_settings',
172 $name . '_site_connection',
173 $name . '_account',
174 ];
175 }
176
177 /**
178 * Drop a snapshot.
179 */
180 public static function delete(SourcePlugin $source): bool
181 {
182 return delete_option(self::optionName($source));
183 }
184 }
185