{"slug":"wordpress-hooks-actions-filters","title":"WordPress Actions and Filters (Hook System)","tags":["hooks","actions","filters","functions.php","php"],"agent_summary":"WordPress hook system: add_action and add_filter syntax, priority and argument count, remove_action and remove_filter, common hook names (wp_head, save_post, pre_get_posts), and creating custom hooks with do_action and apply_filters.","trigger_phrases":["wordpress hooks","add_action wordpress","add_filter wordpress","functions.php hooks","wordpress actions filters","do_action custom","apply_filters custom","wp_head hook"],"runnable":true,"markdown":"\nWordPress's hook system allows modifying behavior without editing core files. Actions DO things; filters TRANSFORM values.\n\n## Actions\n\n```php\n// Basic action\nadd_action('wp_head', 'add_custom_analytics');\nfunction add_custom_analytics() {\n    echo '<script>/* GA4 snippet */</script>';\n}\n\n// Remove a core action\nremove_action('wp_head', 'wp_generator');\n\n// Action with priority (lower = earlier) and argument count\nadd_action('save_post', 'on_post_save', 10, 2);\nfunction on_post_save($post_id, $post) {\n    if ($post->post_type !== 'service') return;\n    // Clear cache, ping API, etc.\n}\n```\n\n### Common Action Hooks\n\n| Hook | Fires When |\n|------|-----------|\n| `init` | Early initialization — register CPTs, taxonomies |\n| `wp_enqueue_scripts` | Enqueue frontend scripts/styles |\n| `admin_enqueue_scripts` | Enqueue admin scripts/styles |\n| `wp_head` | Output to `<head>` |\n| `wp_footer` | Output before `</body>` |\n| `save_post` | After post saved to DB |\n| `template_redirect` | Before template loads — redirect logic |\n| `widgets_init` | Register sidebars and widgets |\n| `after_setup_theme` | Theme features (add_theme_support) |\n\n## Filters\n\n```php\n// Append CTA to single post content\nadd_filter('the_content', 'append_cta_to_posts');\nfunction append_cta_to_posts($content) {\n    if (!is_single()) return $content;\n    return $content . '<div class=\"cta-box\"><a href=\"/contact/\">Get a Free Quote</a></div>';\n}\n\n// Modify excerpt length (default 55 words)\nadd_filter('excerpt_length', function() { return 30; });\n\n// Change wp_mail sender name\nadd_filter('wp_mail_from_name', function() { return 'My Company'; });\n\n// Modify login redirect by role\nadd_filter('login_redirect', function($url, $request, $user) {\n    if (isset($user->roles) && in_array('editor', $user->roles)) {\n        return admin_url('edit.php');\n    }\n    return $url;\n}, 10, 3);\n```\n\n### Common Filter Hooks\n\n| Hook | What It Filters |\n|------|----------------|\n| `the_content` | Post body before display |\n| `the_title` | Post title before display |\n| `excerpt_length` | Auto-excerpt word count |\n| `upload_mimes` | Allowed upload file types |\n| `pre_get_posts` | WP_Query before DB — modify main query |\n| `wp_mail_from` | From email address |\n| `login_redirect` | Post-login redirect URL |\n\n## Custom Hooks\n\n```php\n// Define in plugin/theme — lets third-party code hook in\ndo_action('myplugin_after_lead_saved', $lead_id, $lead_data);\n$price = apply_filters('myplugin_service_price', $base_price, $service_id);\n\n// Third-party hooks in\nadd_action('myplugin_after_lead_saved', 'send_lead_to_crm', 10, 2);\nadd_filter('myplugin_service_price', 'apply_member_discount', 10, 2);\n```\n\n## Priority Rules\n\n- Default priority: 10\n- Lower number = fires earlier\n- Same priority + same hook = fires in order added\n- `remove_action` / `remove_filter` must match the exact priority used when adding\n","html":"<p>WordPress's hook system allows modifying behavior without editing core files. Actions DO things; filters TRANSFORM values.</p>\n<h2>Actions</h2>\n<pre><code class=\"language-php\">// Basic action\nadd_action('wp_head', 'add_custom_analytics');\nfunction add_custom_analytics() {\n    echo '&#x3C;script>/* GA4 snippet */&#x3C;/script>';\n}\n\n// Remove a core action\nremove_action('wp_head', 'wp_generator');\n\n// Action with priority (lower = earlier) and argument count\nadd_action('save_post', 'on_post_save', 10, 2);\nfunction on_post_save($post_id, $post) {\n    if ($post->post_type !== 'service') return;\n    // Clear cache, ping API, etc.\n}\n</code></pre>\n<h3>Common Action Hooks</h3>\n<p>| Hook | Fires When |\n|------|-----------|\n| <code>init</code> | Early initialization — register CPTs, taxonomies |\n| <code>wp_enqueue_scripts</code> | Enqueue frontend scripts/styles |\n| <code>admin_enqueue_scripts</code> | Enqueue admin scripts/styles |\n| <code>wp_head</code> | Output to <code>&#x3C;head></code> |\n| <code>wp_footer</code> | Output before <code>&#x3C;/body></code> |\n| <code>save_post</code> | After post saved to DB |\n| <code>template_redirect</code> | Before template loads — redirect logic |\n| <code>widgets_init</code> | Register sidebars and widgets |\n| <code>after_setup_theme</code> | Theme features (add_theme_support) |</p>\n<h2>Filters</h2>\n<pre><code class=\"language-php\">// Append CTA to single post content\nadd_filter('the_content', 'append_cta_to_posts');\nfunction append_cta_to_posts($content) {\n    if (!is_single()) return $content;\n    return $content . '&#x3C;div class=\"cta-box\">&#x3C;a href=\"/contact/\">Get a Free Quote&#x3C;/a>&#x3C;/div>';\n}\n\n// Modify excerpt length (default 55 words)\nadd_filter('excerpt_length', function() { return 30; });\n\n// Change wp_mail sender name\nadd_filter('wp_mail_from_name', function() { return 'My Company'; });\n\n// Modify login redirect by role\nadd_filter('login_redirect', function($url, $request, $user) {\n    if (isset($user->roles) &#x26;&#x26; in_array('editor', $user->roles)) {\n        return admin_url('edit.php');\n    }\n    return $url;\n}, 10, 3);\n</code></pre>\n<h3>Common Filter Hooks</h3>\n<p>| Hook | What It Filters |\n|------|----------------|\n| <code>the_content</code> | Post body before display |\n| <code>the_title</code> | Post title before display |\n| <code>excerpt_length</code> | Auto-excerpt word count |\n| <code>upload_mimes</code> | Allowed upload file types |\n| <code>pre_get_posts</code> | WP_Query before DB — modify main query |\n| <code>wp_mail_from</code> | From email address |\n| <code>login_redirect</code> | Post-login redirect URL |</p>\n<h2>Custom Hooks</h2>\n<pre><code class=\"language-php\">// Define in plugin/theme — lets third-party code hook in\ndo_action('myplugin_after_lead_saved', $lead_id, $lead_data);\n$price = apply_filters('myplugin_service_price', $base_price, $service_id);\n\n// Third-party hooks in\nadd_action('myplugin_after_lead_saved', 'send_lead_to_crm', 10, 2);\nadd_filter('myplugin_service_price', 'apply_member_discount', 10, 2);\n</code></pre>\n<h2>Priority Rules</h2>\n<ul>\n<li>Default priority: 10</li>\n<li>Lower number = fires earlier</li>\n<li>Same priority + same hook = fires in order added</li>\n<li><code>remove_action</code> / <code>remove_filter</code> must match the exact priority used when adding</li>\n</ul>\n"}