PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.9.1
WP 2FA – Two-factor authentication for WordPress v2.9.1
1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Utils / class-abstract-migration.php
wp-2fa / includes / classes / Utils Last commit date
class-abstract-migration.php 11 months ago class-date-time-utils.php 11 months ago class-debugging.php 11 months ago class-generate-modal.php 11 months ago class-migration.php 11 months ago class-request-utils.php 11 months ago class-settings-utils.php 11 months ago class-user-utils.php 11 months ago class-validator.php 11 months ago class-white-label.php 11 months ago index.php 11 months ago
class-abstract-migration.php
238 lines
1 <?php
2 /**
3 * Abstract migration class.
4 *
5 * @package wp2fa
6 * @subpackage utils
7 * @copyright 2025 Melapress
8 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
9 * @link https://wordpress.org/plugins/wp-2fa/
10 *
11 * @since 1.6
12 */
13
14 declare(strict_types=1);
15
16 namespace WP2FA\Utils;
17
18 use WP2FA\Utils\Settings_Utils;
19
20 defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
21
22 /**
23 * Abstract AMigration class
24 */
25 if ( ! class_exists( '\WP2FA\Utils\Abstract_Migration' ) ) {
26
27 /**
28 * Utility class to ease the migration process.
29 *
30 * Every migration must go in its own method
31 * The naming convention is migrateUpTo_XXX where XXX is the number of the version,
32 * format is numbers only.
33 * Example: migration for version upto 1.4 must be in migrateUpTo_14 method
34 *
35 * The numbers in the names of the methods must have exact numbers count as in the selected
36 * version in use, even if there are silent numbers for some of the major versions as 1, 2, 3 etc. (the .0.0 is skipped / silent)
37 * Example:
38 * - if X.X.X is selected for version number, then for version 1.1 method must have "...migrateUpTo_110..." in its name
39 * - if X.X is selected for version number, then for version 1, method must have "...migrateUpTo_10..." in its name
40 *
41 * Note: you can add prefix to the migration method, if that is necessary, but "migrateUpTo_" is a must -
42 * the name must contain that @see getAllMigrationMethodsAsNumbers of that class.
43 * For version extraction the number following the last '_' will be used
44 * TODO: the mandatory part of the method name can be a setting in the class, but is that a good idea?
45 *
46 * Note: order of the methods is not preserved - version numbers will be used for ordering
47 *
48 * @package WP2FA\Utils
49 *
50 * @since 1.6
51 */
52 class Abstract_Migration {
53
54 /**
55 * Extracted version from the DB (WP option)
56 *
57 * @var string
58 *
59 * @since 1.6.0
60 */
61 protected static $stored_version = '';
62
63 /**
64 * The name of the option from which we should extract version
65 * Note: version is expected in version format - 1.0.0; 1; 1.0; 1.0.0.0
66 * Note: only numbers will be processed
67 *
68 * @var string
69 *
70 * @since 1.6.0
71 */
72 protected static $version_option_name = '';
73
74 /**
75 * The constant name where the plugin version is stored
76 * Note: version is expected in version format - 1.0.0; 1; 1.0; 1.0.0.0
77 * Note: only numbers will be processed
78 *
79 * @var string
80 *
81 * @since 2.2.0
82 */
83 protected static $const_name_of_plugin_version = '';
84
85 /**
86 * Used for adding proper pads for the missing numbers
87 * Version number format used here depends on selection for how many numbers will be used for representing version
88 *
89 * For X.X use 2;
90 * For X.X.X use 3;
91 * For X.X.X.X use 4;
92 *
93 * etc.
94 *
95 * Example: if selected version format is X.X.X that means that 3 digits are used for versioning.
96 * And current version is stored as 2 (no suffix 0.0) that means that it will be normalized as 200.
97 *
98 * @var integer
99 *
100 * @since 1.6.0
101 */
102 protected static $pad_length = 3;
103
104 /**
105 * Collects all the migration methods which needs to be executed in order and executes them
106 *
107 * @return void
108 *
109 * @since 1.6.0
110 */
111 public static function migrate() {
112
113 if ( version_compare( static::get_stored_version(), \constant( static::$const_name_of_plugin_version ), '<' ) ) {
114
115 $stored_version_as_number = static::normalize_version( static::get_stored_version() );
116 $target_version_as_number = static::normalize_version( \constant( static::$const_name_of_plugin_version ) );
117 $method_as_version_numbers = static::get_all_migration_methods_as_numbers();
118
119 $migrate_methods = array_filter(
120 $method_as_version_numbers,
121 function ( $method, $key ) use ( &$stored_version_as_number, &$target_version_as_number ) {
122 if ( $target_version_as_number > $stored_version_as_number ) {
123 return ( in_array( $key, range( $stored_version_as_number, $target_version_as_number ), true ) );
124 }
125
126 return false;
127 },
128 ARRAY_FILTER_USE_BOTH
129 );
130
131 if ( ! empty( $migrate_methods ) ) {
132 \ksort( $migrate_methods );
133 foreach ( $migrate_methods as $method ) {
134 static::{$method}();
135 }
136 }
137
138 // If we have a previous version, its an update so flag notice.
139 if ( ! empty( Settings_Utils::get_option( static::$version_option_name ) ) ) {
140 Settings_Utils::update_option( 'wp_2fa_update_redirection_needed', true );
141 }
142
143 self::store_updated_version();
144 }
145
146 /**
147 * Downgrading the plugin? Set the version number.
148 * Leave the rest as is.
149 *
150 * @return void
151 *
152 * @since 2.2.0
153 */
154 if ( version_compare( static::get_stored_version(), \constant( static::$const_name_of_plugin_version ), '>' ) ) {
155 self::store_updated_version();
156 }
157 }
158
159 /**
160 * Extracts currently stored version from the DB
161 *
162 * @return string
163 *
164 * @since 1.6.0
165 */
166 private static function get_stored_version() {
167
168 if ( '' === trim( (string) static::$stored_version ) ) {
169 static::$stored_version = (string) Settings_Utils::get_option( static::$version_option_name, '0.0.0' );
170 }
171
172 return static::$stored_version;
173 }
174
175 /**
176 * Stores the version to which we migrated
177 *
178 * @return void
179 *
180 * @since 1.6.0
181 */
182 private static function store_updated_version() {
183 Settings_Utils::update_option( static::$version_option_name, \constant( static::$const_name_of_plugin_version ) );
184 }
185
186 /**
187 * Normalized the version numbers to numbers
188 *
189 * Version format is expected to be as follows:
190 * X.X.X
191 *
192 * All non numeric values will be removed from the version string
193 *
194 * Note: version is expected in version format - 1.0.0; 1; 1.0; 1.0.0.0
195 * Note: only numbers will be processed
196 *
197 * @param string $version - The version string we have to use.
198 *
199 * @return string
200 *
201 * @since 1.6.0
202 */
203 private static function normalize_version( string $version ) {
204 $version_as_number = (int) filter_var( $version, FILTER_SANITIZE_NUMBER_INT );
205
206 if ( self::$pad_length > strlen( (string) $version_as_number ) ) {
207 $version_as_number = str_pad( (string) $version_as_number, static::$pad_length, '0', STR_PAD_RIGHT );
208 }
209
210 return $version_as_number;
211 }
212
213 /**
214 * Collects all the migration methods from the class and stores them in the array
215 * Array is in following format:
216 * key - number of the version
217 * value - name of the method
218 *
219 * @return array
220 *
221 * @since 1.6.0
222 */
223 private static function get_all_migration_methods_as_numbers() {
224 $class_methods = \get_class_methods( get_called_class() );
225
226 $method_as_version_numbers = array();
227 foreach ( $class_methods as $method ) {
228 if ( false !== \strpos( $method, 'migrate_up_to_' ) ) {
229 $ver = \substr( $method, \strrpos( $method, '_' ) + 1, \strlen( $method ) );
230 $method_as_version_numbers[ $ver ] = $method;
231 }
232 }
233
234 return $method_as_version_numbers;
235 }
236 }
237 }
238