PluginProbe ʕ •ᴥ•ʔ
Starter Sites & Templates by Neve / 1.4.1
Starter Sites & Templates by Neve v1.4.1
1.4.1 1.4.0 1.3.0 1.2.29 1.2.28 1.2.6 1.2.7 1.2.8 1.2.9 trunk 1.0.10 1.0.11 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.20 1.1.21 1.1.22 1.1.23 1.1.24 1.1.25 1.1.26 1.1.27 1.1.28 1.1.29 1.1.3 1.1.30 1.1.31 1.1.32 1.1.33 1.1.34 1.1.35 1.1.36 1.1.37 1.1.38 1.1.39 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.3 1.2.4 1.2.5
templates-patterns-collection / vendor / codeinwp / themeisle-sdk / src / Modules / Migrator.php
templates-patterns-collection / vendor / codeinwp / themeisle-sdk / src / Modules Last commit date
About_us.php 3 weeks ago Abstract_Migration.php 2 months ago Announcements.php 2 months ago Compatibilities.php 2 years ago Dashboard_widget.php 3 weeks ago Featured_plugins.php 1 month ago Float_widget.php 1 year ago Licenser.php 2 months ago Logger.php 10 months ago Migrator.php 2 months ago Notification.php 3 years ago Promotions.php 3 weeks ago Recommendation.php 3 years ago Review.php 1 year ago Rollback.php 1 year ago Script_loader.php 1 year ago Translate.php 5 years ago Translations.php 1 year ago Uninstall_feedback.php 2 days ago Welcome.php 2 years ago
Migrator.php
178 lines
1 <?php
2 /**
3 * The migrator module for ThemeIsle SDK.
4 *
5 * @package ThemeIsleSDK
6 * @subpackage Modules
7 * @copyright Copyright (c) 2024, Themeisle
8 * @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
9 * @since 3.3.50
10 */
11
12 namespace ThemeisleSDK\Modules;
13
14 use ThemeisleSDK\Common\Abstract_Module;
15 use ThemeisleSDK\Product;
16
17 // Exit if accessed directly.
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit;
20 }
21
22 /**
23 * Migrator module for ThemeIsle SDK.
24 *
25 * Allows products to ship PHP migration files that run automatically on
26 * admin page loads. Each product opts in by registering its migrations
27 * directory via the `{product_slug}_sdk_migrations_path` filter.
28 */
29 class Migrator extends Abstract_Module {
30 /**
31 * Option key suffix used to store the list of ran migrations.
32 */
33 const OPTION_SUFFIX = '_ran_migrations';
34
35 /**
36 * Check if we should load the module for this product.
37 *
38 * Always returns true — the actual path check happens lazily at admin_init.
39 *
40 * @param Product $product Product to load the module for.
41 *
42 * @return bool
43 */
44 public function can_load( $product ) {
45 return apply_filters( $product->get_slug() . '_sdk_enable_migrator', true );
46 }
47
48 /**
49 * Load module logic.
50 *
51 * @param Product $product Product to load.
52 *
53 * @return Migrator
54 */
55 public function load( $product ) {
56 $this->product = $product;
57 add_action( 'admin_init', array( $this, 'run_pending' ) );
58 add_action( 'themeisle_sdk_rollback_migration_' . $product->get_slug(), array( $this, 'rollback' ) );
59 return $this;
60 }
61
62 /**
63 * Discover and run any pending migrations for the product.
64 *
65 * Only runs when a version upgrade was detected during this request, indicated
66 * by the themeisle_sdk_update_{slug} action having fired.
67 *
68 * @return void
69 */
70 public function run_pending() {
71 if ( ! did_action( 'themeisle_sdk_update_' . $this->product->get_slug() ) ) {
72 return;
73 }
74
75 $path = $this->get_migrations_path();
76
77 if ( empty( $path ) || ! is_dir( $path ) ) {
78 return;
79 }
80
81 $files = glob( trailingslashit( $path ) . '*.php' );
82
83 if ( empty( $files ) ) {
84 return;
85 }
86
87 sort( $files ); // Alphabetical order = chronological order given timestamp naming.
88
89 $option_key = $this->product->get_key() . self::OPTION_SUFFIX;
90 $ran = get_option( $option_key, array() );
91
92 foreach ( $files as $file ) {
93 $name = basename( $file, '.php' );
94
95 if ( in_array( $name, $ran, true ) ) {
96 continue;
97 }
98
99 try {
100 $migration = require $file; // Migration files return an anonymous class instance.
101
102 if ( ! ( $migration instanceof Abstract_Migration ) ) {
103 continue;
104 }
105
106 if ( ! $migration->should_run() ) {
107 continue;
108 }
109
110 $migration->up();
111 $ran[] = $name;
112 update_option( $option_key, $ran );
113 } catch ( \Throwable $e ) {
114 // Log and stop — leave the migration unrecorded so it retries next load.
115 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
116 error_log( 'ThemeIsle SDK Migrator: failed to run ' . $name . ': ' . $e->getMessage() );
117 break;
118 }
119 }
120 }
121
122 /**
123 * Roll back a single migration by name.
124 *
125 * Calls down() on the migration and removes it from the ran list so it will
126 * be picked up again on the next upgrade. This method is never called
127 * automatically — products invoke it explicitly when needed.
128 *
129 * @param string $migration_name Migration basename without .php extension.
130 *
131 * @return bool True if rolled back successfully, false if not found or not previously run.
132 */
133 public function rollback( $migration_name ) {
134 $option_key = $this->product->get_key() . self::OPTION_SUFFIX;
135 $ran = get_option( $option_key, array() );
136
137 if ( ! in_array( $migration_name, $ran, true ) ) {
138 return false;
139 }
140
141 $path = $this->get_migrations_path();
142 $file = trailingslashit( $path ) . $migration_name . '.php';
143
144 if ( ! is_file( $file ) ) {
145 return false;
146 }
147
148 try {
149 $migration = require $file;
150
151 if ( ! ( $migration instanceof Abstract_Migration ) ) {
152 return false;
153 }
154
155 $migration->down();
156 update_option( $option_key, array_values( array_diff( $ran, array( $migration_name ) ) ) );
157
158 return true;
159 } catch ( \Throwable $e ) {
160 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
161 error_log( 'ThemeIsle SDK Migrator: failed to roll back ' . $migration_name . ': ' . $e->getMessage() );
162
163 return false;
164 }
165 }
166
167 /**
168 * Get the migrations directory path for the current product.
169 *
170 * Products register their path via the `{slug}_sdk_migrations_path` filter.
171 *
172 * @return string Absolute path to the migrations directory, or empty string.
173 */
174 private function get_migrations_path() {
175 return (string) apply_filters( $this->product->get_slug() . '_sdk_migrations_path', '' );
176 }
177 }
178