PluginProbe
BeyondWords – AI audio for publishers / 7.0.0
BeyondWords – AI audio for publishers v7.0.0
7.1.0 trunk 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.1.0 4.1.1 4.1.2 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.3.0 4.4.0 4.5.0 4.5.1 4.6.0 4.6.1 4.6.2 4.7.0 All 43 releases
speechkit / src / core / class-uninstaller.php

class-uninstaller.php in BeyondWords – AI audio for publishers 7.0.0, at src/core/class-uninstaller.php

146 lines 3.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Uninstall cleanup: deletes every BeyondWords option, transient and post-meta row.
4 *
5 * @package BeyondWords\Core
6 * @since 3.7.0
7 * @since 7.0.0 Refactored to BeyondWords namespace with snake_case methods.
8 */
9
10 declare( strict_types = 1 );
11
12 namespace BeyondWords\Core;
13
14 defined( 'ABSPATH' ) || exit;
15
16 /**
17 * Database cleanup helpers used during plugin uninstall.
18 *
19 * @since 7.0.0 Refactored to BeyondWords namespace with snake_case methods.
20 */
21 class Uninstaller {
22
23 /**
24 * Run the full uninstall cleanup for every site on the install.
25 *
26 * `uninstall.php` runs once, in the main site's context, yet every value is
27 * stored per-site — so on multisite each site must be visited in turn.
28 *
29 * @since 7.0.0
30 *
31 * @return void
32 */
33 public static function run(): void {
34 if ( ! is_multisite() ) {
35 self::cleanup_site();
36 return;
37 }
38
39 // `number => 0` lifts the default 100-site cap.
40 $site_ids = get_sites(
41 [
42 'fields' => 'ids',
43 'number' => 0,
44 ]
45 );
46
47 foreach ( $site_ids as $site_id ) {
48 switch_to_blog( (int) $site_id );
49 self::cleanup_site();
50 restore_current_blog();
51 }
52 }
53
54 /**
55 * Delete every BeyondWords value for the current site.
56 *
57 * @since 7.0.0
58 *
59 * @return void
60 */
61 private static function cleanup_site(): void {
62 self::cleanup_plugin_transients();
63 self::cleanup_plugin_options();
64 self::cleanup_custom_fields();
65 }
66
67 /**
68 * Delete every BeyondWords transient.
69 *
70 * @return int Rows deleted.
71 */
72 public static function cleanup_plugin_transients(): int {
73 global $wpdb;
74
75 // Transients are stored as `_transient_<key>` + `_transient_timeout_<key>`
76 // option pairs; sweep both prefixes or the timeout rows are orphaned.
77 // phpcs:ignore WordPress.DB.DirectDatabaseQuery
78 $count = $wpdb->query(
79 "DELETE FROM $wpdb->options
80 WHERE `option_name` LIKE '_transient_beyondwords_%'
81 OR `option_name` LIKE '_transient_timeout_beyondwords_%'"
82 );
83
84 return (int) $count;
85 }
86
87 /**
88 * Delete every BeyondWords plugin option (current + deprecated).
89 *
90 * @return int Options deleted.
91 */
92 public static function cleanup_plugin_options(): int {
93 $options = Utils::get_options( 'all' );
94 $total = 0;
95
96 foreach ( $options as $option ) {
97 // Options are stored per-site via `update_option()`, so `delete_option()`
98 // is the correct call on both single-site and multisite.
99 if ( delete_option( $option ) ) {
100 ++$total;
101 }
102
103 // Defensive: a legacy install may have stored a matching network option.
104 if ( is_multisite() ) {
105 delete_site_option( $option );
106 }
107 }
108
109 return $total;
110 }
111
112 /**
113 * Delete every BeyondWords post-meta value.
114 *
115 * Deletes by meta_id in per-key batches — a single `DELETE … WHERE meta_key
116 * IN (…)` can lock the postmeta table for unacceptably long.
117 *
118 * @return int Meta rows deleted.
119 */
120 public static function cleanup_custom_fields(): int {
121 global $wpdb;
122
123 // The create lock is absent from the key list, but a fatal can strand a row.
124 $fields = array_merge( Utils::get_post_meta_keys( 'all' ), [ \BeyondWords\Post\Sync::CREATE_LOCK_META_KEY ] );
125 $total = 0;
126
127 foreach ( $fields as $field ) {
128 // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared
129 $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT `meta_id` FROM {$wpdb->postmeta} WHERE `meta_key` = %s;", $field ) );
130
131 if ( empty( $meta_ids ) ) {
132 continue;
133 }
134
135 // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared
136 $count = $wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE `meta_id` IN ( " . implode( ',', array_map( 'intval', $meta_ids ) ) . ' );' );
137
138 if ( $count ) {
139 $total += (int) $count;
140 }
141 }
142
143 return $total;
144 }
145 }
146