get_col( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $wpdb->prefix . 'tagloom_' ) . '%' ) ); foreach ( (array) $tagloom_tables as $tagloom_table ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange -- Uninstall is where a plugin is expected to drop its own tables. $wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', (string) $tagloom_table ) ); } /* * Transients are options too, under two rows each, so they come out of the same * sweep — the value and its timeout. */ $tagloom_option_names = $wpdb->get_col( $wpdb->prepare( 'SELECT option_name FROM %i WHERE option_name LIKE %s OR option_name LIKE %s OR option_name LIKE %s', $wpdb->options, $wpdb->esc_like( 'tagloom_' ) . '%', $wpdb->esc_like( '_transient_tagloom_' ) . '%', $wpdb->esc_like( '_transient_timeout_tagloom_' ) . '%' ) ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching /* * Removed through delete_option() rather than one DELETE, so a persistent * object cache is invalidated on the way. A raw DELETE leaves `alloptions` * holding values whose rows are gone, which a later reinstall reads back as * settings nobody set. */ foreach ( (array) $tagloom_option_names as $tagloom_option_name ) { delete_option( (string) $tagloom_option_name ); } } /* * On a network, every site — not just the one the network admin happens to be * on. Without this loop a network uninstall removed the main site's tables and * options and left every other site's behind; ShareCrew and Gloty closed the * same gap. get_sites() with 'number' => 0 lifts the default 100-site cap. * Tagloom stores nothing network-wide, so there is no separate network pass. */ if ( is_multisite() ) { $tagloom_site_ids = get_sites( [ 'fields' => 'ids', 'number' => 0, ] ); foreach ( (array) $tagloom_site_ids as $tagloom_site_id ) { switch_to_blog( (int) $tagloom_site_id ); tagloom_uninstall_site(); restore_current_blog(); } } else { tagloom_uninstall_site(); } /* * DELIBERATELY NOT REMOVED: the per-order-item traceability meta. * * When Tagloom prints a label it stamps the values it printed onto the order * item, so the order keeps a permanent record of which batch each unit was * filled from. That is a record about goods that physically shipped to a * customer. It belongs to the order rather than to the plugin, and for a food, * cosmetics or fragrance seller it may be the one thing a regulator asks for. * * Deleting it because somebody uninstalled a printing tool would destroy * evidence they are required to keep. Leaving a few rows of orphaned meta * behind is the cheaper mistake by a wide margin. * * It is also why the sweeps above are scoped to `tagloom_`-prefixed names: the * stamp lives on the order item under `_tagloom_printed`, which no prefix query * for options or tables can reach. That is deliberate, not an oversight — see * `UninstallCoverageTest::test_the_traceability_stamp_is_left_alone()`. */