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