prefix`, no `plugixacrm_`) the add-ons created. * * Grouped by the plugin that owned them so the list can be audited against a repository if any of * this ever has to be reconstructed. * * @return string[] */ private static function legacy_tables(): array { return array( // plugixa-billing — the nine money modules. 'products', 'product_categories', 'brands', 'tax_classes', 'quotes', 'quote_lines', 'sales_orders', 'sales_order_lines', 'invoices', 'invoice_lines', 'credit_notes', 'credit_note_lines', 'payments', 'payment_allocations', 'payment_requests', 'payment_request_invoices', 'subscriptions', 'subscription_lines', 'subscription_periods', // plugixa-inventory. 'warehouses', 'inventory_transactions', 'inventory_numbers', 'inventory_documents', 'inventory_document_lines', 'attributes', 'attribute_options', 'product_attributes', 'price_books', 'price_rules', // plugixa-purchasing. 'suppliers', 'purchase_orders', 'purchase_order_lines', 'bills', 'bill_lines', 'bill_credits', 'bill_credit_lines', 'return_orders', 'return_order_lines', // plugixa-projects. 'projects', 'project_stages', 'milestones', 'project_tasks', 'project_task_dependencies', 'project_task_history', 'project_members', // plugixa-scheduler. 'scheduler_booking_types', 'scheduler_booking_type_hosts', 'scheduler_availability', 'scheduler_availability_overrides', 'scheduler_bookings', ); } /** * Drop what the add-ons left, once. * * @param ModuleRegistry|null $modules The installed modules, used as the authority on what must * NOT be dropped. When null the check is skipped and nothing * is dropped, because a sweep with no authority to check * against is exactly the dangerous case. * * @return string[] The bare names actually dropped. */ public static function run( ?ModuleRegistry $modules = null ): array { if ( get_option( self::DONE_OPTION ) ) { return array(); } if ( null === $modules ) { return array(); } $claimed = self::claimed_tables( $modules ); $dropped = array(); foreach ( self::legacy_tables() as $bare ) { if ( isset( $claimed[ $bare ] ) ) { // A live module owns this name. The list is out of date, not the module — say so // loudly and leave the data alone. self::warn( $bare, $claimed[ $bare ] ); continue; } if ( ! Schema::exists( $bare ) ) { continue; } Schema::drop( $bare ); $dropped[] = $bare; } foreach ( self::LEGACY_OPTIONS as $option ) { delete_option( $option ); } update_option( self::DONE_OPTION, PLUGIXA_CRM_VERSION ); return $dropped; } /** * Every bare table name the installed modules declare. * * Read from the modules themselves rather than from a second list here, so this cannot drift. * * @param ModuleRegistry $modules The installed modules. * * @return array Bare table name => the module class that claims it. */ private static function claimed_tables( ModuleRegistry $modules ): array { $claimed = array(); foreach ( $modules->all() as $module ) { $schema = str_replace( '\\Module', '\\Schema', $module::class ); if ( ! class_exists( $schema ) || ! method_exists( $schema, 'tables' ) ) { continue; } foreach ( (array) $schema::tables() as $bare ) { $claimed[ (string) $bare ] = $schema; } } return $claimed; } /** * @param string $bare The table that was refused. * @param string $owner The class that claims it. */ private static function warn( string $bare, string $owner ): void { if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) { return; } // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- WP_DEBUG-only, and a silent skip here would hide a list that has gone stale against a live module. error_log( sprintf( 'Plugixa CRM: refusing to drop legacy table "%s" — %s claims it. Remove it from LegacyCleanup.', $bare, $owner ) ); } }