| 1 |
<?php |
| 2 |
|
| 3 |
namespace Upress\EzCache; |
| 4 |
|
| 5 |
use Exception; |
| 6 |
use Upress\EzCache\Utilities\Logger; |
| 7 |
use wpdb; |
| 8 |
|
| 9 |
class Updater { |
| 10 |
/** @var string $current_version */ |
| 11 |
protected static $current_version; |
| 12 |
/** @var string $collate */ |
| 13 |
protected static $collate; |
| 14 |
|
| 15 |
protected static function wpdb() { |
| 16 |
global $wpdb; |
| 17 |
} |
| 18 |
|
| 19 |
public static function uninstall() { |
| 20 |
global $wpdb; |
| 21 |
|
| 22 |
Cache::instance()->clear_cache( true ); |
| 23 |
$wpdb->query("DROP TABLE IF EXISTS `{$wpdb->prefix}ezcache_webp_images`"); |
| 24 |
delete_option( 'ezcache_version' ); |
| 25 |
} |
| 26 |
|
| 27 |
/** |
| 28 |
* Run any necessary db updates, file upgrades etc. |
| 29 |
*/ |
| 30 |
public static function upgrade() { |
| 31 |
global $wpdb; |
| 32 |
|
| 33 |
self::$current_version = get_option( 'ezcache_version', 0 ); |
| 34 |
self::$collate = $wpdb->get_charset_collate(); |
| 35 |
|
| 36 |
/** @noinspection PhpIncludeInspection */ |
| 37 |
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); |
| 38 |
|
| 39 |
try { |
| 40 |
if ( version_compare( self::$current_version, '0.1-20190811', '<' ) ) { |
| 41 |
self::update_0_1_20190811(); |
| 42 |
} |
| 43 |
|
| 44 |
if ( version_compare( self::$current_version, '0.1-20190812', '<' ) ) { |
| 45 |
self::update_0_1_20190812(); |
| 46 |
} |
| 47 |
|
| 48 |
if ( version_compare( self::$current_version, '1.5', '<' ) ) { |
| 49 |
self::update_1_5(); |
| 50 |
} |
| 51 |
|
| 52 |
if ( version_compare( self::$current_version, '2.0.0', '<' ) ) { |
| 53 |
self::update_2_0(); |
| 54 |
} |
| 55 |
|
| 56 |
// Always verify the required tables exist and are up to date. This is |
| 57 |
// intentionally NOT gated behind a version match: updates deployed by |
| 58 |
// replacing files (no deactivate/activate cycle) never fire the activation |
| 59 |
// hook, so an unconditional verify_tables() is what lets a missing table |
| 60 |
// self-heal on the next run instead of staying broken forever. |
| 61 |
self::verify_tables(); |
| 62 |
|
| 63 |
// A plugin update can change generated markup/minification, so flush the |
| 64 |
// page cache once per version change. This ensures stale or corrupted |
| 65 |
// cached pages (e.g. JSON-LD altered by an older inline-JS minifier) are |
| 66 |
// regenerated on the next request instead of persisting until TTL expiry. |
| 67 |
if ( version_compare( (string) self::$current_version, EZCACHE_VERSION, '!=' ) && class_exists( '\\Upress\\EzCache\\Cache' ) ) { |
| 68 |
Cache::instance()->clear_cache(); |
| 69 |
} |
| 70 |
|
| 71 |
// make sure we update the version in the database so we can run upgrades at later times |
| 72 |
update_option( 'ezcache_version', EZCACHE_VERSION ); |
| 73 |
} catch ( Exception $ex ) { |
| 74 |
Logger::log( 'ezCache Updater Error: ' . $ex ); |
| 75 |
wp_die( $ex->getMessage() ); |
| 76 |
} |
| 77 |
} |
| 78 |
|
| 79 |
/** |
| 80 |
* Run the upgrade routine when the stored DB version doesn't match the code |
| 81 |
* version. Hooked on admin_init so that file-replacement updates (which skip |
| 82 |
* the activation hook) still create/verify tables the first time an admin |
| 83 |
* page loads, without waiting for a deactivate/activate cycle. |
| 84 |
*/ |
| 85 |
public static function maybe_upgrade() { |
| 86 |
if ( get_option( 'ezcache_version' ) !== EZCACHE_VERSION ) { |
| 87 |
self::upgrade(); |
| 88 |
} |
| 89 |
} |
| 90 |
|
| 91 |
/** |
| 92 |
* Ensure the plugin's tables exist without running the full upgrade flow and |
| 93 |
* without wp_die() on failure. Used as a last-resort self-heal from REST |
| 94 |
* endpoints (e.g. the WebP scan) that need the table to be present. |
| 95 |
*/ |
| 96 |
public static function ensure_tables() { |
| 97 |
global $wpdb; |
| 98 |
|
| 99 |
self::$current_version = get_option( 'ezcache_version', 0 ); |
| 100 |
self::$collate = $wpdb->get_charset_collate(); |
| 101 |
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); |
| 102 |
|
| 103 |
try { |
| 104 |
self::verify_tables(); |
| 105 |
} catch ( Exception $ex ) { |
| 106 |
Logger::log( 'ezCache ensure_tables error: ' . $ex->getMessage() ); |
| 107 |
} |
| 108 |
} |
| 109 |
|
| 110 |
/** |
| 111 |
* Verify all tables exists and run their creation if not |
| 112 |
* @throws Exception |
| 113 |
*/ |
| 114 |
protected static function verify_tables() { |
| 115 |
global $wpdb; |
| 116 |
|
| 117 |
if ( ! $wpdb->get_row( "SHOW TABLES LIKE '{$wpdb->prefix}ezcache_webp_images'" ) ) { |
| 118 |
// this creates the table |
| 119 |
self::update_0_1_20190811(); |
| 120 |
} |
| 121 |
|
| 122 |
// and this makes sure the table has the correct columns |
| 123 |
self::update_0_1_20190812(); |
| 124 |
} |
| 125 |
|
| 126 |
/** |
| 127 |
* Update to the 1.0 version |
| 128 |
* create the 404 database |
| 129 |
* @throws Exception |
| 130 |
*/ |
| 131 |
protected static function update_0_1_20190811() { |
| 132 |
global $wpdb; |
| 133 |
|
| 134 |
// Note: TEXT columns must not carry a DEFAULT (rejected before MySQL 8.0.13) |
| 135 |
// and datetime columns must not default to the zero date '0000-00-00' |
| 136 |
// (rejected under the NO_ZERO_DATE / strict SQL mode used by MySQL 8+). |
| 137 |
// Both are made nullable — every INSERT already sets these values explicitly. |
| 138 |
$wpdb->query( "CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}ezcache_webp_images` ( |
| 139 |
`id` bigint(10) UNSIGNED NOT NULL AUTO_INCREMENT, |
| 140 |
`uid` varchar(191) NOT NULL DEFAULT '', |
| 141 |
`url` text NULL, |
| 142 |
`webp_url` text NULL, |
| 143 |
`status` enum('pending', 'completed', 'failed') NOT NULL DEFAULT 'pending', |
| 144 |
`created_at` datetime NULL DEFAULT NULL, |
| 145 |
`updated_at` datetime NULL DEFAULT NULL, |
| 146 |
PRIMARY KEY (`id`), |
| 147 |
UNIQUE INDEX (`uid`) USING BTREE |
| 148 |
) ". ( self::$collate ) ); |
| 149 |
|
| 150 |
if ( ! empty( $wpdb->last_error ) ) { |
| 151 |
throw new Exception( $wpdb->last_error ); |
| 152 |
} |
| 153 |
} |
| 154 |
|
| 155 |
/** |
| 156 |
* Update to the 1.0 version |
| 157 |
* @throws Exception |
| 158 |
*/ |
| 159 |
protected static function update_0_1_20190812() { |
| 160 |
global $wpdb; |
| 161 |
|
| 162 |
$cols = $wpdb->get_col( "SHOW COLUMNS FROM `{$wpdb->prefix}ezcache_webp_images`", 0 ); |
| 163 |
if( ! in_array( 'path', $cols ) ) { |
| 164 |
$wpdb->query( "ALTER TABLE `{$wpdb->prefix}ezcache_webp_images` |
| 165 |
ADD COLUMN `path` text NULL AFTER `webp_url`, |
| 166 |
ADD COLUMN `webp_path` text NULL AFTER `path`, |
| 167 |
ADD COLUMN `original_size` int(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `webp_path`, |
| 168 |
ADD COLUMN `webp_size` int(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `original_size` |
| 169 |
" ); |
| 170 |
} |
| 171 |
|
| 172 |
if ( ! empty( $wpdb->last_error ) ) { |
| 173 |
throw new Exception( $wpdb->last_error ); |
| 174 |
} |
| 175 |
} |
| 176 |
|
| 177 |
/** |
| 178 |
* Update to the 1.4.2 version |
| 179 |
* @throws Exception |
| 180 |
*/ |
| 181 |
protected static function update_1_5() { |
| 182 |
global $wpdb; |
| 183 |
|
| 184 |
delete_site_option( 'ezcache_convert_images_to_webp_reprocess_queue' ); |
| 185 |
$wpdb->query( "DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE 'wp_ezcache_convert_images_to_webp_batch_%'"); |
| 186 |
|
| 187 |
if ( wp_next_scheduled( 'ezcache_convert_images_to_webp_cron' ) ) { |
| 188 |
wp_unschedule_hook( 'ezcache_convert_images_to_webp_cron' ); |
| 189 |
} |
| 190 |
|
| 191 |
if ( ! empty( $wpdb->last_error ) ) { |
| 192 |
throw new Exception( $wpdb->last_error ); |
| 193 |
} |
| 194 |
} |
| 195 |
|
| 196 |
|
| 197 |
protected static function update_2_0() { |
| 198 |
global $wpdb; |
| 199 |
$table = $wpdb->prefix . "ezcache_webp_images"; |
| 200 |
|
| 201 |
if ( ! $wpdb->get_row( "SHOW TABLES LIKE '{$table}'" ) ) { |
| 202 |
self::update_0_1_20190811(); |
| 203 |
self::update_0_1_20190812(); |
| 204 |
return; |
| 205 |
} |
| 206 |
|
| 207 |
$cols = $wpdb->get_results( "SHOW COLUMNS FROM `{$table}`" ); |
| 208 |
foreach ( $cols as $col ) { |
| 209 |
if ( $col->Field === "original_size" && strpos( $col->Type, "bigint" ) === false ) { |
| 210 |
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `original_size` bigint(20) UNSIGNED NOT NULL DEFAULT 0" ); |
| 211 |
} |
| 212 |
if ( $col->Field === "webp_size" && strpos( $col->Type, "bigint" ) === false ) { |
| 213 |
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `webp_size` bigint(20) UNSIGNED NOT NULL DEFAULT 0" ); |
| 214 |
} |
| 215 |
} |
| 216 |
|
| 217 |
$indexes = $wpdb->get_results( "SHOW INDEX FROM `{$table}` WHERE Column_name = 'status'" ); |
| 218 |
if ( empty( $indexes ) ) { |
| 219 |
$wpdb->query( "ALTER TABLE `{$table}` ADD INDEX `status_idx` (`status`)" ); |
| 220 |
} |
| 221 |
} |
| 222 |
|
| 223 |
} |