En hook är en plats i WordPress där du får köra egen kod utan att ändra i kärnan, temat eller pluginet. Det finns två sorter. En action gör något, till exempel skriver ut HTML eller skickar ett mejl. Ett filter tar emot ett värde, ändrar det och lämnar tillbaka det. Det är hela skillnaden, och den avgör hur du skriver din funktion.
Här är ett filter som jag använder i de flesta butiker. WooCommerce skickar ämnesraden för orderbekräftelsen genom woocommerce_email_subject_customer_processing_order, och jag byter ut den. Koden ligger i ett eget litet plugin eller i child-temats functions.php.
<?php
// Child-temats functions.php eller ett eget plugin.
// Priority 10 is the default. The 2 says we want two of the three values WooCommerce passes: the subject and the WC_Order.
add_filter( 'woocommerce_email_subject_customer_processing_order', 'wpu_processing_subject', 10, 2 );
function wpu_processing_subject( $subject, $order ) {
return sprintf( 'Tack, vi har tagit emot order %s', $order->get_order_number() );
}
Funktionen får ämnesraden som första argument och ordern som andra, och den måste returnera en sträng. Glömmer du return blir ämnesraden tom. Det är det vanligaste nybörjarfelet med filter, och det ger inget felmeddelande.
Action: lägg till något i sidfoten
En action returnerar inget. Den körs när WordPress når en viss punkt och gör sitt. wp_footer körs strax före </body> i alla teman, och jag använder den när en butik vill visa organisationsnummer och kundtjänsttider på alla sidor utan att temat har en plats för det.
<?php
// Child-temats functions.php.
add_action( 'wp_footer', 'wpu_footer_company_info' );
function wpu_footer_company_info() {
// The theme's footer template is locked in the block editor, so the line is printed here instead.
echo '<p class="wpu-company-info">Exempelbolaget AB, org.nr 556000-0000. Kundtjänst vardagar 9–16.</p>';
}
Tekniskt är add_action och add_filter samma funktion. I wp-includes/plugin.php anropar add_action bara add_filter. Skillnaden ligger i hur WordPress sedan kör hooken: apply_filters använder returvärdet, do_action bryr sig inte om det.
Prioritet och argument
Alla funktioner på samma hook körs i prioritetsordning, lägst först, och vid samma prioritet i den ordning de registrerades. Standard är 10. WordPress eget the_content är det tydligaste exemplet: wpautop ligger på 10 och do_shortcode på 11, så shortcodes körs efter att styckena fått sina <p>.
I WooCommerce styr prioriteten hela produktsidans layout. På woocommerce_single_product_summary ligger titeln på 5, priset på 10, den korta beskrivningen på 20, köpknappen på 30 och artikelnumret på 40. Vill du lägga en leveranstext mellan pris och beskrivning väljer du 15. Listan finns i includes/wc-template-hooks.php i WooCommerce.
Det fjärde argumentet till add_filter säger hur många värden din funktion vill ha. WordPress skickar aldrig fler än du begärt, och begär du fler än hooken ger får du ett PHP-fel om parametern saknar standardvärde. Här är ett fraktfilter där både prioritet och argument spelar roll. Butiken ger fri frakt över ett belopp och vill då dölja de betalda alternativen, men bara för leveranser inom Sverige.
<?php
// Ett eget plugin, eftersom frakt hör till butiken och inte till temat.
// Priority 100 so it runs after shipping plugins that add or rename rates on the same filter.
// Two arguments: the rates and the package, which carries the destination.
add_filter( 'woocommerce_package_rates', 'wpu_only_free_shipping_in_sweden', 100, 2 );
function wpu_only_free_shipping_in_sweden( $rates, $package ) {
if ( 'SE' !== ( $package['destination']['country'] ?? '' ) ) {
return $rates;
}
$free = array();
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->get_method_id() ) {
$free[ $rate_id ] = $rate;
}
}
// No free shipping rate in this cart: leave the paid options untouched.
return empty( $free ) ? $rates : $free;
}
Filtret gäller både blockkassan och den klassiska kassan, eftersom fraktberäkningen sker på servern i båda fallen.
Ta bort det någon annan lagt till
remove_action och remove_filter kräver att du beskriver exakt samma sak som lades till: hooknamn, callback och prioritet. Stämmer inte prioriteten returnerar funktionen false utan att klaga. Det klassiska WooCommerce-exemplet är brödsmulorna, som ligger på woocommerce_before_main_content med prioritet 20. remove_action( 'woocommerce_before_main_content', 'woocommerce_breadcrumb' ) gör ingenting, eftersom standardprioriteten 10 inte matchar. Med 20 som tredje argument försvinner de.
Svårare blir det när callbacken är en metod på ett objekt. WooCommerce skriver ut strukturerade data som ett <script type="application/ld+json"> i sidfoten, registrerat som array( $this, 'output_structured_data' ) inne i klassen WC_Structured_Data. WordPress identifierar en sådan callback med objektets id, så du måste skicka med samma objekt. Det ligger i WC()->structured_data, som skapas när WooCommerce kör sin init på prioritet 0. I en butik där SEO-pluginet redan skriver produktschema tar jag bort WooCommerce egen variant så här.
<?php
// Child-temats functions.php eller ett eget plugin.
// WooCommerce registered the callback on an object, so the same instance is needed. It exists from init priority 0.
add_action( 'init', 'wpu_remove_wc_structured_data' );
function wpu_remove_wc_structured_data() {
if ( ! function_exists( 'WC' ) ) {
return;
}
// The SEO plugin already prints product schema; this avoids a second ld+json block on every product page.
remove_action( 'wp_footer', array( WC()->structured_data, 'output_structured_data' ), 10 );
}
Statiska klassmetoder är enklare. De beskrivs med klassnamnet som sträng, array( 'WC_Frontend_Scripts', 'load_scripts' ), och behöver inget objekt.
Anonyma funktioner går inte att ta bort alls. När ett plugin skriver add_action( 'wp_footer', function () { ... } ) har callbacken bara ett objekt-id som ingen utanför pluginet kan nå. Därför skriver jag alltid egna hooks med namngivna funktioner, så att nästa utvecklare kan koppla bort dem.
Hitta rätt hook
Jag använder tre vägar. Query Monitor har en panel som listar varje hook som körts på sidan, med alla callbacks och deras prioritet. Det är snabbaste sättet att se vem som gör vad, och att kontrollera att din remove_action faktiskt tagit bort något.
Andra vägen är grep i källkoden. Hooks skapas med do_action och apply_filters, så grep -rn "do_action( 'woocommerce_" wp-content/plugins/woocommerce/templates ger alla actions i mallarna, med filnamn och radnummer. Söker du efter ett mejlfilter är grep -rn "apply_filters( 'woocommerce_email_" includes/emails rätt. Du ser också exakt vilka argument hooken skickar, vilket dokumentationen inte alltid gör.
Tredje vägen är developer.wordpress.org, där varje hook i kärnan har en egen sida med argument och exempel. För WooCommerce är filen includes/wc-template-hooks.php den bästa översikten över vilka mallfunktioner som sitter på vilken hook.
Fallgropar
Timing är den fallgrop jag ser oftast. En remove_action måste köras efter att den andra parten gjort sin add_action. Child-temats functions.php laddas efter alla plugin, så WooCommerce mallfunktioner går att ta bort direkt där. Hooks som registreras på init eller senare måste du ta bort på samma hook med högre prioritet, eller på en hook som körs efteråt.
Filter som inte returnerar något är nummer två. Symtomet är en tom ämnesrad, ett tomt pris eller en vit sida utan innehåll, och ingen loggrad. Kontrollera return först.
Nummer tre är att skriva om en hook i tron att den finns. Hooknamn stavas olika mellan WooCommerce-versioner och mellan blockkassan och den klassiska kassan. Verifiera namnet med grep innan du bygger på det. Det tar en minut och sparar en timme.
Vanliga frågor
Vad är skillnaden mellan add_action och add_filter?
Ingen, tekniskt. add_action anropar add_filter rakt av i wp-includes/plugin.php. Skillnaden ligger i hur WordPress sedan använder hooken: på ett filter används returvärdet, på en action kastas det bort.
Måste jag ange prioritet och antal argument?
Nej. Standard är prioritet 10 och ett argument. Du behöver bara ange dem när ordningen spelar roll eller när din funktion vill ha fler värden än det första.
Varför fungerar inte min remove_action?
Nästan alltid en av tre orsaker. Prioriteten stämmer inte med den som användes vid add_action. Koden körs innan den andra parten hunnit registrera sin hook. Eller så är callbacken en klassmetod på ett objekt, och du skickar inte med samma objekt.
Ska koden ligga i functions.php eller i ett plugin?
Hör den till utseendet lägger jag den i child-temats functions.php. Hör den till butiken, som mejl och frakt, lägger jag den i ett eget litet plugin så att den överlever ett temabyte.
Kan jag ta bort en hook som lagts till med en anonym funktion?
Inte med remove_action, eftersom du inte kan beskriva funktionen. Det går att leta upp den i $wp_filter och ta bort den därifrån, men det är skört och jag undviker det. Skriv egna hooks med namngivna funktioner så att andra kan ta bort dem.
