base_prefix ); foreach ( self::SUFFIXES as $suffix ) { if ( ! self::allowed( $connection, $origin ) ) { return self::error(); } $table = $connection->base_prefix . $suffix; if ( ! preg_match( '/\A[a-zA-Z0-9_]{1,64}\z/D', $table ) ) { return self::error(); } if ( false === $connection->query( $connection->prepare( 'DROP TABLE IF EXISTS %i', $table ) ) ) { return new \WP_Error( 'request_rule_cleanup', 'An owned custom-rule table could not be removed. Explicit deletion remains available for retry.' ); } $found = $connection->get_var( $connection->prepare( 'SHOW TABLES LIKE %s', $connection->esc_like( $table ) ) ); if ( '' !== $connection->last_error || null !== $found || ! self::allowed( $connection, $origin ) ) { return new \WP_Error( 'request_rule_cleanup', 'Custom-rule table removal could not be verified. Retry without assuming completion.' ); } } return true; } /** * Recheck original native scope, held lifecycle mutex and persisted installation deletion consent. * * @phpstan-impure Reads mutable native authority, persisted state or current process context. * * @param \wpdb $connection Original native connection owning the lifecycle cleanup. * @param array $origin Captured site, principal and base-prefix values for the original native connection. * * @return bool Whether the captured connection remains authorized to remove owned configuration. */ private static function allowed( \wpdb $connection, array $origin ): bool { global $wpdb; /** * Current WordPress database adapter used by this operation. * * @var \wpdb $wpdb */ if ( $wpdb !== $connection || array( get_current_blog_id(), get_current_user_id(), $wpdb->base_prefix ) !== $origin ) { return false; } $lock = $connection->get_var( $connection->prepare( 'SELECT IS_USED_LOCK(%s)=CONNECTION_ID()', Derived::lock_name() ) ); if ( '' !== $connection->last_error || '1' !== (string) $lock ) { return false; } $sql = is_multisite() ? $connection->prepare( "SELECT meta_value FROM %i WHERE site_id=%d AND meta_key='logged_ai_delete_all_on_uninstall' LIMIT 1", $connection->sitemeta, get_main_network_id() ) : "SELECT option_value FROM $connection->options WHERE option_name='logged_ai_delete_all_on_uninstall' LIMIT 1"; $choice = $connection->get_var( $sql ); return '' === $connection->last_error && in_array( $choice, array( '1', 'b:1;' ), true ) && $wpdb === $connection && array( get_current_blog_id(), get_current_user_id(), $wpdb->base_prefix ) === $origin; } /** * Create the fixed missing-authority error for optional request-rule deletion. * * @return \WP_Error Structured cleanup error preserving the requirement for current explicit consent. */ private static function error(): \WP_Error { return new \WP_Error( 'request_rule_cleanup', 'Custom-rule deletion requires the current explicit installation choice and held lifecycle lock.' ); } }