WordPress-Plugin-Konflikte und ein langsames WooCommerce-Admin: Plugins bei Massenbearbeitungen isolieren
Ein WooCommerce plugin conflict kündigt sich selten an. Bei einem Shop mit sechzig aktiven Plugins zeigt sich das Symptom als langsames WooCommerce-Admin und eine Massenoperation, die sich anders verhält als auf einer sauberen Installation: Sie zieht sich hin, bleibt manchmal auf halbem Weg stehen, schreibt gelegentlich nur einen Teil der Produkte und meldet für den Rest einen Fehler. Der Bulk-Editor ist selten die Ursache. Jedes Plugin auf der Seite hängt seine eigenen Callbacks an save_post und woocommerce_update_product, und wenn BEAR dreitausend Produkte speichert, laufen all diese Callbacks jeweils dreitausend Mal. Ein Page-Builder, der seinen Cache neu aufbaut, ein SEO-Plugin, das Scores neu berechnet, ein Suchplugin, das neu indexiert, ein Sync-Plugin, das eine externe API aufruft: Keines davon wurde mit einer Massenschreibung im Hinterkopf geschrieben.
Zu viele Plugins auf einer WordPress-Seite sind kein Problem, das man löst, indem man sie deaktiviert, denn man braucht sie den Rest des Tages. Es ist ein Problem, das man für jeweils eine Anfrage löst.
WordPress hat eine Stelle, an der sich das steuern lässt, und sie liegt früher, als die meisten erwarten.
Warum ein WordPress plugin conflict nicht aus functions.php heraus behoben werden kann
Der übliche Rat bei einem WordPress plugin conflict lautet, Plugins nacheinander zu deaktivieren, bis das Symptom verschwindet. Das findet den Übeltäter und lässt Sie mit einer Entscheidung zurück, die Sie nicht treffen wollen, weil das Plugin, das ausgeschaltet bleiben muss, auch ein Plugin ist, das der Shop braucht. WordPress lädt Plugins, bevor es das Theme lädt. Wenn functions.php läuft, ist bereits jede Plugin-Datei eingebunden und jeder Hook bereits registriert. Callbacks an dieser Stelle zu entfernen bedeutet, ihnen einzeln mit remove_action hinterherzujagen, und Sie werden immer einige übersehen: Closures können nicht namentlich entfernt werden, Prioritäten unterscheiden sich, und ein Plugin, das seine Hooks verzögert registriert, fügt sie hinterher wieder hinzu. Das Ergebnis ist ein halb deaktiviertes Plugin, was schlimmer ist als ein vollständig geladenes.
Es gibt einen Hook, der läuft, bevor Plugins geladen werden. WordPress liest die Liste der aktiven Plugins aus der Option active_plugins und bindet jede Datei aus dieser Liste ein. Die Option durchläuft zuerst den Filter option_active_plugins. Gibt man dort ein kürzeres Array zurück, werden die übrigen Plugin-Dateien überhaupt nicht gelesen. Nicht deaktiviert, nicht stummgeschaltet: nie geladen.
Code, der laufen muss, bevor Plugins geladen werden, kann nicht selbst in einem Plugin liegen. Er liegt in wp-content/mu-plugins/. WordPress Must-Use-Plugins werden geladen, bevor irgendetwas aus der regulären Plugin-Liste geladen wird, sie können nicht aus dem Admin deaktiviert werden, und jede PHP-Datei, die direkt in diesem Ordner liegt, läuft automatisch. Das ist früh genug, damit der obige Filter noch greift.
WordPress Must-Use-Plugins laden zuerst
Speichern Sie dies als wp-content/mu-plugins/bear-isolation.php. Falls der WordPress-mu-plugins-Ordner auf Ihrer Seite noch nicht existiert, legen Sie ihn an: Es ist ein gewöhnliches Verzeichnis innerhalb von wp-content, und jede PHP-Datei darin wird automatisch geladen. Danach ist nichts zu aktivieren.
<?php
/**
* Plugin Name: BEAR Plugin Isolation
* Description: Loads a reduced set of plugins during BEAR bulk editor and MCP requests.
* Version: 1.0
*
* Place this file into wp-content/mu-plugins/
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Mode:
* 'disable' - load everything EXCEPT the plugins listed below. Safer, start here.
* 'keep_only' - load ONLY the plugins listed below. Fastest, needs testing.
*/
define( 'BEAR_ISOLATION_MODE', 'disable' );
/**
* The list the mode above applies to. Paths are exactly as they appear
* in the active_plugins option: folder/file.php
*/
function bear_isolation_list() {
$list = array(
// 'elementor/elementor.php',
// 'js_composer/js_composer.php',
// 'contact-form-7/wp-contact-form-7.php',
// 'wp-smushit/wp-smush.php',
);
return apply_filters( 'bear_isolation_list', $list );
}
/**
* Plugins that stay loaded whatever the mode and the list say.
* Without WooCommerce and BEAR itself the request cannot work at all.
*/
function bear_isolation_always_keep() {
$keep = array(
'woocommerce/woocommerce.php',
'woo-bulk-editor/index.php', // same folder for the free and the paid build
);
return apply_filters( 'bear_isolation_always_keep', $keep );
}
/**
* Is this a request the isolation should apply to?
*
* REST routing has not run yet at this point, so the raw URI is the only
* signal available for the MCP endpoint.
*/
function bear_isolation_is_target_request() {
$uri = isset( $_SERVER['REQUEST_URI'] ) ? (string) $_SERVER['REQUEST_URI'] : '';
// The MCP endpoint: pretty permalinks and the plain query form.
if ( strpos( $uri, '/woobe/v1/mcp' ) !== false
|| strpos( $uri, 'rest_route=/woobe/v1/mcp' ) !== false ) {
return true;
}
// The bulk editor admin page.
if ( isset( $_GET['page'] ) && strpos( (string) $_GET['page'], 'woobe' ) === 0 ) {
return true;
}
// The editor's own ajax calls: woobe_get_products, woobe_bulk_edit and the rest.
if ( isset( $_REQUEST['action'] ) && strpos( (string) $_REQUEST['action'], 'woobe' ) === 0 ) {
return true;
}
return false;
}
/**
* Regular single-site activation list: values are plugin paths.
*/
function bear_isolation_filter_plugins( $plugins ) {
if ( ! is_array( $plugins ) || ! bear_isolation_is_target_request() ) {
return $plugins;
}
$keep = bear_isolation_always_keep();
$list = bear_isolation_list();
if ( BEAR_ISOLATION_MODE === 'keep_only' ) {
$allowed = array_merge( $keep, $list );
return array_values( array_intersect( $plugins, $allowed ) );
}
// 'disable': drop the listed ones, but never the ones that must stay.
$drop = array_diff( $list, $keep );
return array_values( array_diff( $plugins, $drop ) );
}
add_filter( 'option_active_plugins', 'bear_isolation_filter_plugins', 1 );
/**
* Multisite: network-activated plugins live in another option, and there the
* plugin path is the KEY, not the value. Hence the _key variants.
*/
function bear_isolation_filter_network_plugins( $plugins ) {
if ( ! is_array( $plugins ) || ! bear_isolation_is_target_request() ) {
return $plugins;
}
$keep = bear_isolation_always_keep();
$list = bear_isolation_list();
if ( BEAR_ISOLATION_MODE === 'keep_only' ) {
$allowed = array_flip( array_merge( $keep, $list ) );
return array_intersect_key( $plugins, $allowed );
}
$drop = array_flip( array_diff( $list, $keep ) );
return array_diff_key( $plugins, $drop );
}
add_filter( 'site_option_active_sitewide_plugins', 'bear_isolation_filter_network_plugins', 1 );
Die zwei Modi
disable ist der Modus, mit dem man beginnt. Alles lädt wie gewohnt, außer den Plugins, die Sie benennen. Fügen Sie die schweren hinzu, die bei einer Produktschreibung nichts zu suchen haben: Page-Builder, Slider, Galerien, Formular-Plugins, Analytics, Chat-Widgets, Bildoptimierer, Backup-Plugins. Jedes, das Sie hinzufügen, ist ein Satz Callbacks weniger, der pro Produkt läuft.
keep_only ist das Gegenteil und der schnellere der beiden Modi: Nichts lädt außer WooCommerce, BEAR und was Sie auflisten. Bei einer Seite mit sechzig Plugins macht das aus einer Massenschreibung etwas, das einer sauberen Installation nahekommt. Es entfernt auch Plugins, die Sie während der Schreibung eventuell tatsächlich brauchen, behandeln Sie die Liste also als etwas, das Sie durch Testen aufbauen, nicht durch Raten.
Beide Modi schützen WooCommerce und BEAR über bear_isolation_always_keep(), sodass ein Tippfehler in der Liste die Anfrage nicht zerstören kann. Der Plugin-Ordner ist woo-bulk-editor sowohl für die kostenlose als auch für die kostenpflichtige Version, sodass der obige Pfad für beide gleich ist. Falls Sie sich beim Pfad eines anderen Plugins unsicher sind, ist er unter Plugins im wp-admin sichtbar, oder in der Option active_plugins.
Was die Isolation abdeckt
Drei Arten von Anfragen, und Sie können sie eingrenzen, indem Sie bear_isolation_is_target_request() bearbeiten:
- Der MCP-Endpunkt,
/wp-json/woobe/v1/mcpund seine?rest_route=-Form. Hier schreibt ein KI-Assistent Produkte, und hier ist ein Plugin-Konflikt am schwersten zu bemerken, weil niemand auf den Bildschirm schaut. - Die Bulk-Editor-Seite selbst,
edit.php?post_type=product&page=woobe. Das ist diejenige, die sichtbar schneller lädt: Das Admin zieht nicht mehr jede Metabox und jedes Admin-Skript jedes Plugins mit ein. - Die ajax-Aufrufe des Editors, jede Action, die mit
woobebeginnt. Hier findet das eigentliche Schreiben statt, hier hätten also die Callbacks gefeuert.
Wenn Sie die Isolation nur für MCP wollen und nicht für die Editor-Seite, löschen Sie die zweite und dritte Prüfung. Der Rest der Datei funktioniert weiter.
Was Sie verlieren, und das ist der Teil, über den man nachdenken sollte
Ein Plugin, das nicht geladen wird, reagiert nicht darauf, dass ein Produkt gespeichert wird. Meistens ist genau das der Sinn der Sache. Manchmal ist es ein Problem, und es ist besser zu wissen, welches, bevor man eine Operation auf den gesamten Katalog anwendet.
Suche und Synchronisation. Relevanssi, ElasticPress, Algolia, Feed-Generatoren, ERP-Anbindungen: Sie aktualisieren ihren Index oder senden Daten bei save_post. Isolieren Sie sie, und dreitausend Produkte ändern sich in der Datenbank, während der Index noch die alten beschreibt. Bauen Sie den Index anschließend neu auf, oder lassen Sie diese Plugins geladen.
Mehrsprachigkeit. WPML und Polylang erstellen und verknüpfen Übersetzungen, wenn ein Produkt gespeichert wird. Ohne sie landet die Schreibung nur in der Ausgangssprache, und die Verbindungen zwischen den Übersetzungen können auseinanderlaufen.
Von anderen Plugins definierte Felder. ACF, Subscriptions, Bookings, individuelle Metaboxen: Wenn das Plugin nicht geladen ist, werden seine Felder nicht registriert, und BEAR zeigt sie nicht als Spalten an. Die Operation wird nichts beschädigen, aber ein Feld, das Sie zu bearbeiten erwartet haben, wird nicht da sein.
Caches. Cache-Plugins invalidieren Seiten beim Speichern. Isoliert tun sie das nicht, und die Shop-Front zeigt weiterhin die alten Preise, bis der Cache abläuft oder Sie ihn von Hand leeren.
Sicherheits-Plugins. Eine Firewall in der disable-Liste ist eine Firewall, die bei dieser Anfrage nicht läuft. Der MCP-Endpunkt hat sein eigenes Token und, bei aktivierter Zwei-Faktor-Verbindung, seine eigene zweite Prüfung, aber das ist eine Entscheidung, die man bewusst treffen sollte und nicht versehentlich.
Zwei Dinge kann diese Methode überhaupt nicht abschalten: andere mu-plugins und das aktive Theme. Beide laden außerhalb von active_plugins. Wenn die functions.php Ihres Themes den schwersten Code der Seite trägt, hilft die Isolation dabei nicht.
Es testen, bevor Sie ihm vertrauen
Arbeiten Sie zuerst auf einer Staging-Kopie, mit einem kleinen Filter statt dem gesamten Katalog.
- Installieren Sie die Datei mit einer leeren Liste und bestätigen Sie, dass sich nichts ändert. Die Seite muss sich genau wie vorher verhalten.
- Fügen Sie ein Plugin zur Liste hinzu, öffnen Sie die Bulk-Editor-Seite und prüfen Sie, ob die Spalten, die Sie normalerweise verwenden, noch da sind.
- Führen Sie eine Massenoperation an zehn Produkten aus und vergleichen Sie das Ergebnis mit dem, was Sie erwarten: Preise geschrieben, Lagerbestand geschrieben, die Shop-Front zeigt nach einer Cache-Leerung die neuen Werte.
- Fügen Sie das nächste Plugin hinzu und wiederholen Sie das. Wenn etwas verschwindet oder aufhört, sich zu aktualisieren, ist das zuletzt hinzugefügte Plugin die Ursache.
Um die Isolation vollständig zu entfernen, löschen Sie die Datei. Es gibt nichts zu deaktivieren und nichts, was in der Datenbank zurückbleibt.
Wann sich das lohnt: ein langsames WooCommerce-Admin, und wann nicht
Bei einem Shop mit einem Dutzend Plugins: nein. WordPress kommt damit gut zurecht, und der Gewinn lohnt die Datei nicht. Es lohnt sich, wo die Plugin-Liste lang wird, wo eine Massenoperation über Tausende von Produkten in ein Timeout läuft oder nur teilweise fertig wird, oder wo ein KI-Assistent über den MCP-Server schreibt und niemand auf den Bildschirm schaut, um zu bemerken, dass ein Sync-Plugin im Hintergrund achttausend API-Aufrufe abgefeuert hat.
Es ist auch die ehrliche Antwort auf „wie beschleunige ich das WooCommerce-Admin", wenn der eigentliche Grund die Anzahl der Plugins ist und nicht der Server: Statt nach dem einen schuldigen Plugin zu suchen, entscheiden Sie, welche überhaupt etwas damit zu tun haben sollten, geladen zu sein, während Produkte geschrieben werden.