PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / 3.6.5.2
JetFormBuilder — Dynamic Blocks Form Builder v3.6.5.2
3.6.5.2 3.6.5.1 3.6.5 3.6.4.2 3.6.4.1 3.6.4 3.6.3.1 3.6.3 3.6.2.2 3.6.2.1 3.6.2 3.6.1.1 3.6.1 3.6.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.0 2.1.1 2.1.10 2.1.11 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 3.0.0 3.0.0.1 3.0.0.2 3.0.0.3 3.0.1 3.0.1.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.0.1 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.3.1 3.3.4 3.3.4.1 3.3.4.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.5.1 3.4.5.2 3.4.6 3.4.7 3.4.7.1 3.5.0 3.5.1 3.5.1.1 3.5.1.2 3.5.2 3.5.2.1 3.5.3 3.5.4 3.5.5 3.5.6 3.5.6.1 3.5.6.2 3.5.6.3 3.6.0
jetformbuilder / includes / migrations / auto-migrator.php
jetformbuilder / includes / migrations Last commit date
profilers 2 years ago versions 4 days ago auto-migrator.php 4 days ago migration-exception.php 2 years ago migration-model.php 2 years ago migrator.php 4 days ago view-migrations.php 2 years ago
auto-migrator.php
183 lines
1 <?php
2
3
4 namespace Jet_Form_Builder\Migrations;
5
6 use Jet_Form_Builder\Classes\Instance_Trait;
7 use Jet_Form_Builder\Db_Queries\Execution_Builder;
8 use Jet_Form_Builder\Migrations\Versions\Version_3_6_5_2;
9
10 // If this file is called directly, abort.
11 if ( ! defined( 'WPINC' ) ) {
12 die;
13 }
14
15 /**
16 * Runs outstanding migrations automatically once the plugin files are updated to a
17 * newer version, without waiting for a manual "Update DB" click (that notice only
18 * covers form-record table migrations, not general ones like `Version_3_6_5_2`) and
19 * without relying on `register_activation_hook` (which does NOT fire on plugin update,
20 * only on manual (re)activation).
21 *
22 * Only migrations explicitly returned by `migration_instances()` are auto-run. This is
23 * intentionally separate from `Migrator::install()`: older schema migrations can be
24 * expensive and may require a deliberate/manual update, so an unrelated admin request
25 * must not start the full historical migration set.
26 *
27 * The selected migration classes stay in this registry permanently. Therefore a client
28 * may skip the release that introduced `Version_3_6_5_2` and update directly to 3.7.0 (or
29 * later): its stored DB version is still older than the running plugin, the auto-migrator
30 * runs, and `Base_Migration::install()` executes every selected migration that is not yet
31 * recorded in the migrations table. `Version_3_6_5_2` performs only one bounded batch in
32 * this request; a capable admin page processes the remainder through short AJAX requests
33 * using persisted progress, while per-form lazy initialization keeps frontend submissions
34 * working in the meantime.
35 *
36 * @since 3.6.5.2
37 */
38 class Auto_Migrator {
39
40 use Instance_Trait;
41
42 const DB_VERSION_OPTION = 'jet_fb_db_version';
43 const LOCK_TRANSIENT = 'jet_fb_migrations_running';
44
45 public function init_hooks() {
46 add_action( 'admin_init', array( $this, 'maybe_run' ) );
47 }
48
49 /**
50 * Runs migrations if the plugin code is newer than the last migrated DB version.
51 * Cheap no-op on the vast majority of requests (single option read + version compare).
52 */
53 public function maybe_run() {
54 if ( ! $this->needs_upgrade() ) {
55 return;
56 }
57
58 // Only an admin-context request that can manage the site should trigger schema/data
59 // changes. Other admin requests (e.g. admin-ajax from a low-priv user) just skip;
60 // they'll be picked up on the next capable admin's page load.
61 if ( ! current_user_can( 'manage_options' ) ) {
62 return;
63 }
64
65 // Guard against two overlapping admin requests both running migrations at once.
66 if ( get_transient( self::LOCK_TRANSIENT ) ) {
67 return;
68 }
69
70 set_transient( self::LOCK_TRANSIENT, 1, MINUTE_IN_SECONDS * 5 );
71
72 try {
73 $this->run();
74 } finally {
75 delete_transient( self::LOCK_TRANSIENT );
76 }
77 }
78
79 public function needs_upgrade(): bool {
80 $stored = get_option( self::DB_VERSION_OPTION, '' );
81
82 // No stamp yet: either a genuine update from a pre-3.6.5.2 version (including a
83 // client that skipped straight to a later release), or a fresh install. `run()`
84 // checks the selected migrations individually, so both cases are safe.
85 if ( '' === $stored ) {
86 return true;
87 }
88
89 return version_compare( $stored, JET_FORM_BUILDER_VERSION, '<' );
90 }
91
92 protected function run() {
93 // Nothing outstanding (e.g. fresh install already stamped by table creation) →
94 // just record the version and skip touching the DB in a transaction.
95 if ( $this->all_installed() ) {
96 $this->stamp_version();
97
98 return;
99 }
100
101 try {
102 if ( false === $this->transaction_start() ) {
103 throw new \RuntimeException( 'Failed to start the auto-migration transaction.' );
104 }
105
106 $this->install_migrations();
107
108 if ( false === $this->transaction_commit() ) {
109 throw new \RuntimeException( 'Failed to commit the auto-migration transaction.' );
110 }
111 } catch ( \Throwable $exception ) {
112 $this->transaction_rollback();
113
114 // Do NOT stamp the version on failure — leave `needs_upgrade()` true so the
115 // next capable admin request retries. Surface the reason for support.
116 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
117 error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
118 '[jet-form-builder] auto-migration failed: ' . $exception->getMessage()
119 );
120 }
121
122 return;
123 }
124
125 // Success → record the version so we don't re-scan on every admin load until the
126 // next update bumps the plugin version again.
127 $this->stamp_version();
128 }
129
130 /**
131 * Runs only the migration classes explicitly selected for automatic execution. Each
132 * migration is skipped internally if it is already recorded as installed, so keeping
133 * an old migration in this list is both safe and required for clients that skip the
134 * release where it was introduced.
135 *
136 * @throws \Jet_Form_Builder\Migrations\Migration_Exception
137 */
138 protected function install_migrations() {
139 foreach ( $this->migration_instances() as $migration ) {
140 $migration->install();
141 }
142 }
143
144 protected function all_installed(): bool {
145 foreach ( $this->migration_instances() as $migration ) {
146 if ( ! $migration->is_installed() ) {
147 return false;
148 }
149 }
150
151 return true;
152 }
153
154 /**
155 * Automatic migrations must remain listed in future plugin versions so sites that
156 * jump over the introducing release still receive them. Do not place regular/manual
157 * schema migrations here.
158 *
159 * @return \Jet_Form_Builder\Migrations\Versions\Base_Migration[]
160 */
161 protected function migration_instances(): array {
162 return array(
163 new Version_3_6_5_2(),
164 );
165 }
166
167 protected function stamp_version() {
168 update_option( self::DB_VERSION_OPTION, JET_FORM_BUILDER_VERSION, false );
169 }
170
171 protected function transaction_start() {
172 return Execution_Builder::instance()->transaction_start();
173 }
174
175 protected function transaction_commit() {
176 return Execution_Builder::instance()->transaction_commit();
177 }
178
179 protected function transaction_rollback() {
180 return Execution_Builder::instance()->transaction_rollback();
181 }
182 }
183