PluginProbe
CryptX / trunk
CryptX vtrunk
4.2.0 4.1.1 trunk 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.9 2.0 2.1 2.2 2.3 2.3.1 2.3.2 2.3.3 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 All 92 releases
cryptx / uninstall.php

uninstall.php in CryptX trunk, at uninstall.php

184 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Uninstall routine for CryptX.
4 *
5 * Runs when the plugin is deleted through the WordPress admin (not on
6 * deactivation). It removes everything CryptX has stored: the option 'cryptX',
7 * any transient whose name starts with 'cryptx_', and the user meta that
8 * records who declined to write a review.
9 *
10 * Deliberately uses the WordPress API (delete_option(), delete_transient(),
11 * delete_site_transient()) instead of raw DELETE statements, so object caches
12 * and the *_option hooks see the removal. Only *finding* the transients needs
13 * SQL, because WordPress offers no way to enumerate transients by prefix.
14 *
15 * @package CryptX
16 * @since 4.0.12
17 */
18
19 // Without this constant the file was called directly instead of by WordPress.
20 if (!defined('WP_UNINSTALL_PLUGIN')) {
21 exit;
22 }
23
24 /**
25 * The single option this plugin stores.
26 */
27 const CRYPTX_UNINSTALL_OPTION = 'cryptX';
28
29 /**
30 * Prefix of every transient this plugin may have created.
31 */
32 const CRYPTX_UNINSTALL_TRANSIENT_PREFIX = 'cryptx_';
33
34 if (!function_exists('cryptx_uninstall_clean_site')) {
35 /**
36 * Removes all CryptX data of the current site.
37 *
38 * On multisite this runs once per site, inside switch_to_blog(), so that
39 * $wpdb->options points at that site's table.
40 *
41 * @return void
42 */
43 function cryptx_uninstall_clean_site(): void
44 {
45 global $wpdb;
46
47 delete_option(CRYPTX_UNINSTALL_OPTION);
48
49 // Transients live as two option rows: '_transient_<name>' holds the
50 // value, '_transient_timeout_<name>' its expiry. delete_transient()
51 // removes both, which is why the value rows are handled first.
52 // esc_like() escapes the underscores, otherwise "_" would match any
53 // single character and the pattern would reach into other plugins'
54 // rows. The table name is an identifier and stays out of prepare().
55 $valueLike = $wpdb->esc_like('_transient_' . CRYPTX_UNINSTALL_TRANSIENT_PREFIX) . '%';
56
57 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
58 $transientRows = $wpdb->get_col(
59 $wpdb->prepare("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", $valueLike)
60 );
61
62 foreach ($transientRows as $optionName) {
63 delete_transient(substr($optionName, strlen('_transient_')));
64 }
65
66 // A timeout row whose value row had already expired and been removed
67 // would survive the loop above, so sweep the remaining ones directly.
68 $timeoutLike = $wpdb->esc_like('_transient_timeout_' . CRYPTX_UNINSTALL_TRANSIENT_PREFIX) . '%';
69
70 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
71 $timeoutRows = $wpdb->get_col(
72 $wpdb->prepare("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", $timeoutLike)
73 );
74
75 foreach ($timeoutRows as $optionName) {
76 delete_option($optionName);
77 }
78 }
79 }
80
81 if (!function_exists('cryptx_uninstall_clean_network')) {
82 /**
83 * Removes what CryptX stored for the network rather than for a site.
84 *
85 * Both kinds live in the sitemeta table, not in any site's options table,
86 * so the per-site pass would never see them: the site transients, and the
87 * defaults a newly created site started from.
88 *
89 * @return void
90 */
91 function cryptx_uninstall_clean_network(): void
92 {
93 global $wpdb;
94
95 // The network defaults. Added in 4.2.0, and it was missed here at
96 // first -- which would have left the file's own opening promise
97 // ("removes everything CryptX has stored") untrue on exactly the kind
98 // of installation where somebody notices.
99 //
100 // Written out rather than taken from Admin\NetworkDefaults::OPTION:
101 // WordPress runs this file on its own, with the plugin unloaded, so
102 // there is no class to ask. If that constant is ever renamed, this
103 // line has to be renamed with it -- there is nothing that would
104 // notice on its own.
105 delete_site_option('cryptx_network_defaults');
106
107 $valueLike = $wpdb->esc_like('_site_transient_' . CRYPTX_UNINSTALL_TRANSIENT_PREFIX) . '%';
108
109 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
110 $rows = $wpdb->get_col(
111 $wpdb->prepare(
112 "SELECT meta_key FROM {$wpdb->sitemeta} WHERE site_id = %d AND meta_key LIKE %s",
113 get_current_network_id(),
114 $valueLike
115 )
116 );
117
118 foreach ($rows as $metaKey) {
119 delete_site_transient(substr($metaKey, strlen('_site_transient_')));
120 }
121 }
122 }
123
124 if (!function_exists('cryptx_uninstall_clean_users')) {
125 /**
126 * Removes the "do not ask me for a review again" mark from every user.
127 *
128 * Runs exactly once, not once per site: user meta lives in one table for
129 * the whole network, so the per-site pass would delete the same rows over
130 * and over -- and on a network of a thousand sites, a thousand times.
131 *
132 * delete_metadata() with $delete_all rather than a loop over get_users():
133 * the loop would pull every user of the installation into memory to find
134 * the few who ever saw the notice.
135 *
136 * @return void
137 */
138 function cryptx_uninstall_clean_users(): void
139 {
140 // Spelled out rather than taken from Admin\ReviewNotice::USER_META,
141 // for the same reason as the network option below: WordPress runs this
142 // file with the plugin unloaded, so there is no class to ask.
143 delete_metadata('user', 0, 'cryptx_review_dismissed', '', true);
144 }
145 }
146
147 cryptx_uninstall_clean_users();
148
149 if (is_multisite()) {
150 // Work in batches instead of loading every site of a large network at once:
151 // get_sites() without a limit would pull all of them into memory.
152 $cryptx_uninstall_batch_size = 100;
153 $cryptx_uninstall_offset = 0;
154
155 do {
156 $cryptx_uninstall_site_ids = get_sites([
157 'fields' => 'ids',
158 'number' => $cryptx_uninstall_batch_size,
159 'offset' => $cryptx_uninstall_offset,
160 'orderby' => 'id',
161 'update_site_meta_cache' => false,
162 ]);
163
164 foreach ($cryptx_uninstall_site_ids as $cryptx_uninstall_site_id) {
165 switch_to_blog($cryptx_uninstall_site_id);
166 cryptx_uninstall_clean_site();
167 restore_current_blog();
168 }
169
170 $cryptx_uninstall_offset += $cryptx_uninstall_batch_size;
171 } while (count($cryptx_uninstall_site_ids) === $cryptx_uninstall_batch_size);
172
173 cryptx_uninstall_clean_network();
174
175 unset(
176 $cryptx_uninstall_batch_size,
177 $cryptx_uninstall_offset,
178 $cryptx_uninstall_site_ids,
179 $cryptx_uninstall_site_id
180 );
181 } else {
182 cryptx_uninstall_clean_site();
183 }
184