PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 4.7.1
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v4.7.1
4.7.2 4.7.1 4.7.0 4.6.6 4.6.5 4.6.4 4.6.3 4.6.2 4.6.1 4.6.0 4.5.1 4.5.0 4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 1.0.6 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.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.11.1 1.11.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.10.0 2.10.1 2.11.0 2.11.1 2.11.2 2.11.3 2.11.4 2.12.0 2.13.0 2.14.0 2.14.1 2.15.0 2.15.1 2.16.0 2.16.1 2.16.2 2.16.3 2.17.0 2.17.1 2.17.2 2.18.0 2.19.0 2.19.2 2.19.3 2.19.4 2.2.0 2.2.1 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.22.0 2.22.1 2.23.0 2.24.0 2.25.0 2.25.1 2.25.2 2.26.0 2.27.0 2.27.1 2.28.0 2.29.0 2.29.1 2.29.2 2.29.3 2.29.4 2.3.0 2.3.1 2.30.0 2.31.0 2.31.1 2.31.2 2.31.3 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.40.0 2.40.1 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.9.0 3.0.0 3.0.0-RC1 3.0.0-RC2 3.0.0-beta1 3.0.0-beta2 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.10.0 3.10.1 3.11.0 3.12.0 3.13.0 3.13.1 3.13.2 3.13.3 3.13.4 3.14.0 3.15.0 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.16.6 3.16.7 3.16.8 3.17.0 3.17.1 3.17.2 3.17.3 3.17.4 3.17.5 3.17.6 3.18.0 3.19.0 3.19.1 3.19.2 3.2.0 3.2.1 3.2.2 3.20.0 3.20.1 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 4.0.0 4.0.1 4.0.2 4.0.3 trunk 4.1.0 0.2.19.1 4.1.1 1.0.0 4.2.0 1.0.1 1.0.2
surecart / app / src / Database / VersionMigration.php
surecart / app / src / Database Last commit date
Tables 1 year ago CartLineItemDetailsMigrationService.php 1 day ago GeneralMigration.php 1 year ago MigrationsServiceProvider.php 1 month ago ProductPageMigrationService.php 1 day ago RewriteRulesMigrationService.php 1 year ago Table.php 4 years ago ThemeMigrationService.php 5 months ago UpdateMigrationServiceProvider.php 2 months ago UserMetaMigrationsService.php 4 years ago VersionMigration.php 1 day ago WebhookMigrationsService.php 1 year ago
VersionMigration.php
167 lines
1 <?php
2
3 namespace SureCart\Database;
4
5 /**
6 * A migration that will run each time the version of the plugin changes.
7 */
8 abstract class VersionMigration {
9 /**
10 * The key for the migration.
11 *
12 * @var string
13 */
14 protected $migration_key = 'surecart_migration_version';
15
16 /**
17 * Set from run() when a post update fails; complete() then defers marking
18 * so the next admin_init retries.
19 *
20 * @var bool
21 */
22 protected $failed = false;
23
24 /**
25 * Failed runs allowed before giving up — otherwise a permanently failing
26 * save would retry on every admin_init forever.
27 *
28 * @var int
29 */
30 const MAX_FAILED_ATTEMPTS = 5;
31
32 /**
33 * Run on init.
34 *
35 * @return void
36 */
37 public function bootstrap() {
38 add_action( 'admin_init', [ $this, 'maybeRun' ] );
39 }
40
41 /**
42 * Get the current version of the plugin.
43 *
44 * @return string
45 */
46 public function currentVersion() {
47 return \SureCart::plugin()->version();
48 }
49
50 /**
51 * Maybe let's run the migration.
52 *
53 * @return void
54 */
55 public function maybeRun() {
56 if ( ! $this->shouldMigrate() ) {
57 return;
58 }
59
60 // run the migration.
61 $this->run();
62
63 // update the migration complete on admin_init complete, after all migrations have run.
64 add_action( 'admin_init', [ $this, 'complete' ], 999999 );
65 }
66
67 /**
68 * Should we run this migration?
69 *
70 * @return boolean
71 */
72 public function shouldMigrate(): bool {
73 // check if we already have done this migration.
74 return version_compare( $this->currentVersion(), $this->getLastMigrationVersion(), '!=' );
75 }
76
77 /**
78 * Run the migration
79 *
80 * @return void
81 */
82 protected function run() {
83 }
84
85 /**
86 * Save migrated post content without kses mangling it.
87 *
88 * Migrations re-save content that already passed a privileged save plus
89 * plugin-generated block markup, so kses must not run — admin_init also
90 * fires on logged-out admin-ajax requests, where kses would strip <style>
91 * and <script> tags. wp_update_post() also expects slashed data, so we
92 * slash to keep backslashes (e.g. CSS "\f101", block attribute JSON) intact.
93 *
94 * Callers must only pass content derived from what is already stored plus
95 * plugin-generated markup — never fresh user, customer, or webhook input,
96 * which would inherit the kses bypass.
97 *
98 * @param int $post_id The post to update.
99 * @param string $content The migrated post content.
100 * @return int|\WP_Error The post ID on success, WP_Error on failure.
101 */
102 protected function updatePostContent( int $post_id, string $content ) {
103 // remember the actual prior state — kses_init() on restore would recompute
104 // from capability and undo a kses another plugin forced on.
105 $had_kses = false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' );
106
107 // disables kses process-wide, so scope it per save (don't hoist around
108 // callers' loops) — the toggle is cheap, the narrow window is the point.
109 kses_remove_filters();
110
111 try {
112 $result = wp_update_post(
113 wp_slash(
114 array(
115 'ID' => $post_id,
116 'post_content' => $content,
117 )
118 ),
119 true
120 );
121 } finally {
122 // a throwing save_post hook must not leave kses disabled for the rest of the request.
123 if ( $had_kses ) {
124 kses_init_filters();
125 }
126 }
127
128 return $result;
129 }
130
131 /**
132 * Store the current plugin version when complete.
133 *
134 * A failed run defers completion so the next admin_init retries; after
135 * MAX_FAILED_ATTEMPTS we mark complete anyway and stop. The counter resets
136 * on completion, so the next version bump gets a fresh retry budget.
137 *
138 * @return void
139 */
140 public function complete() {
141 $attempts_key = $this->migration_key . '_failed_attempts';
142
143 if ( $this->failed ) {
144 $attempts = (int) get_option( $attempts_key, 0 ) + 1;
145
146 if ( $attempts < self::MAX_FAILED_ATTEMPTS ) {
147 update_option( $attempts_key, $attempts, false );
148 return;
149 }
150
151 error_log( 'SureCart: ' . $this->migration_key . ' migration gave up after ' . $attempts . ' failed attempts — some content may still be on the old markup.' );
152 }
153
154 delete_option( $attempts_key );
155 update_option( $this->migration_key, $this->currentVersion(), false ); // don't autoload since we are only doing this on the admin.
156 }
157
158 /**
159 * Get the last version there was a migration.
160 *
161 * @return string
162 */
163 public function getLastMigrationVersion() {
164 return get_option( $this->migration_key, '0.0.0' );
165 }
166 }
167