{"id":135745,"date":"2015-01-07T08:00:19","date_gmt":"2015-01-07T13:00:19","guid":{"rendered":"http:\/\/premium.wpmudev.org\/blog\/?p=135745"},"modified":"2015-12-09T00:59:25","modified_gmt":"2015-12-09T05:59:25","slug":"wordpress-hooks","status":"publish","type":"post","link":"https:\/\/wpmudev.com\/blog\/wordpress-hooks\/","title":{"rendered":"A Quick (and in-Depth) Guide to WordPress Hooks"},"content":{"rendered":"<p>WordPress hooks are <em>the<\/em> thing to learn if you want to get into WordPress development. Hooks allows you to do two things: change default functionality within WordPress and add your own functionality without modifying core WordPress files at all.<\/p>\n<p>Making sure you never modify core files is extremely important, but modifying and adding stuff seems impossible without it.<\/p>\n<p>In this post, I&#8217;m going to show you how you can do it pretty easily with two types of hooks: actions and filters. You&#8217;ll need a basic understanding of HTML and PHP for this one, but not too much. Let&#8217;s dig in!<\/p>\n<h3>The Need for Hooks<\/h3>\n<p>Hooks are built into\u00a0WordPress and are used to modify or add functionality to the core system. Let&#8217;s assume for a moment that WordPress does not provide any hooks. And let&#8217;s also say you work with a lot of scheduled posts and you would like to email yourself when a post is published. How would you go about doing that?<\/p>\n<p>Without hooks you would need to modify core files. You would hunt down the code that is responsible for publishing a post and just after the function that performs the action, you would paste your own code.<\/p>\n<p>This is detrimental for a number of reasons. The two big ones are updates and uncontrollable code. If you modify core WordPress files your code will work just fine but will be erased when you update WordPress to the next version. You would have to remember or track all of your changes and then put them back in. Not exactly convenient. Alternatively, you could simply not update WordPress, but that would be a huge security risk in the long run.<\/p>\n<p>Even if you do manage to keep track of changes and update WordPress, you are left with a non-standard environment with uncontrollable code. Other\u00a0WordPress developers will have a hard time dealing with your code, and the community as a whole will not be happy.<\/p>\n<h3>Enter Hooks<\/h3>\n<p>A hook does almost exactly what we\u00a0talked about\u00a0before (find the place where\u00a0a\u00a0post is published, paste your code), but replaces the horrible pasting bit with a placeholder. By default,\u00a0there is a placeholder after the post publishing function, which allows you to tie functionality there. Here&#8217;s a shortened version of the actual <em><code>wp_publish_post(),<\/code><\/em> which you can find in <em><a href=\"https:\/\/core.trac.wordpress.org\/browser\/tags\/4.0.1\/src\/wp-includes\/post.php#L0\" target=\"_blank\">wp-includes\/post.php<\/a><\/em>.<\/p>\n<div class=\"gist\" data-gist=\"61f99825caff867fb22f569fe6e310a9\" data-gist-file=\"pseudo-code.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/61f99825caff867fb22f569fe6e310a9.js?file=pseudo-code.php\">Loading gist 61f99825caff867fb22f569fe6e310a9<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>That <em><code>do_action()<\/code><\/em> bit basically says: You can tie your own function to me, they will all be executed. So whenever WordPress publishes a post using the <em><code>wp_publish_post()<\/code><\/em> function it goes through all the necessary steps to make it happen. It then moves on to the <em><code>do_action()<\/code><\/em> function. It&#8217;s first parameter is <em><code>wp_insert_post<\/code><\/em>. Any function which is tied to this <em><code>wp_insert_post<\/code><\/em> action will get executed.<\/p>\n<p>Let&#8217;s look at an example.<\/p>\n<p>To create a hooked function you need to create a simple plugin, use your theme&#8217;s <em>functions.php<\/em> file, or create a child theme and use the <em>functions.php<\/em> file in there. Let&#8217;s create a quick plugin now.<\/p>\n<p>Create a new folder in the Plugins directory and name it &#8220;hook-example.&#8221; Create a &#8220;<em>hook-example.php<\/em>&#8221; file within that directory and paste the following code into it:<\/p>\n<div class=\"gist\" data-gist=\"d7ccf506f333b4b58c0eb3d29d7344da\" data-gist-file=\"plugin.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/d7ccf506f333b4b58c0eb3d29d7344da.js?file=plugin.php\">Loading gist d7ccf506f333b4b58c0eb3d29d7344da<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>Your plugin is now ready. Head on over to the Plugins section in\u00a0WordPress\u00a0admin and activate it. Now let&#8217;s create a hooked function. In the <em><code>hook-example.php<\/code><\/em> file, paste the following:<\/p>\n<div class=\"gist\" data-gist=\"cd4f9ca577b5368ba9341ce256ab7336\" data-gist-file=\"first-hook.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/cd4f9ca577b5368ba9341ce256ab7336.js?file=first-hook.php\">Loading gist cd4f9ca577b5368ba9341ce256ab7336<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>We&#8217;re using the <em><code>add_action()<\/code><\/em> function to let WordPress know that we&#8217;d like to hook a function into <em><code>wp_insert_post<\/code><\/em>. The second and third parameters are the priority and the arguments. We&#8217;ll get into that soon.<\/p>\n<p>In our hooked function we simply define what we&#8217;d like to do. In this case send an email to ourselves. Whenever WordPress published a post it will look for all functions hooked into <em><code>wp_insert_post<\/code><\/em> and run them all.<\/p>\n<p>So what have we achieved here? We&#8217;ve added functionality to a core part of WordPress without actually modifying the core files. All we needed was one extra line of code. This is very important! All you need to do is write a function that does what you need, then you hook it into a specific part of WordPress.<\/p>\n<h3>Actions and Filters<\/h3>\n<p>So far we&#8217;ve seen what an action is \u2013 it&#8217;s a piece of code that\u00a0is executed at a specific time. A filter can be used to modify content before WordPress uses it. You could use a filter to modify the &#8220;your password is incorrect&#8221; text on the login page. Let&#8217;s take a look at <em><code>wp-login.php<\/code><\/em>:<\/p>\n<div class=\"gist\" data-gist=\"a9e159b3b44197220ea52052af5e0cf7\" data-gist-file=\"wp-login.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/a9e159b3b44197220ea52052af5e0cf7.js?file=wp-login.php\">Loading gist a9e159b3b44197220ea52052af5e0cf7<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>The snippet from the actual file that handles logins shows that login error messages are displayed using an <em><code>apply_filters()<\/code><\/em> function. This is very similar in logic to the <em><code>do-action()<\/code><\/em> function.<\/p>\n<p>My second parameter contains some sort of data. If you use a hooked function based on my first parameter you can modify this data, otherwise I&#8217;m going to use it as is. By default, if you enter a correct username but incorrect password you see the following notice:<\/p>\n<div class=\"image-grid cgrid-row\">\n<div class=\"cgrid-col cgrid-col-span-full\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-135747\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2014\/12\/incorrect-password.png\" alt=\"Incorrect password notice\" width=\"341\" height=\"99\" \/><\/div>\n<\/div>\n<p>If you think this is a bit unsafe because it exposes the fact that the username is correct you can hook a function to <em><code>login_errors<\/code><\/em> to modify it. Here&#8217;s how:<\/p>\n<div class=\"gist\" data-gist=\"ae4a778a1372ed3a2ee48b523cac9534\" data-gist-file=\"first-filter.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/ae4a778a1372ed3a2ee48b523cac9534.js?file=first-filter.php\">Loading gist ae4a778a1372ed3a2ee48b523cac9534<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>By implementing this change you&#8217;ve added a bit more security to your login process and you can also control the text.<\/p>\n<div class=\"image-grid cgrid-row\">\n<div class=\"cgrid-col cgrid-col-span-full\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-135749\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2014\/12\/modified_login.png\" alt=\"Modified login form\" width=\"342\" height=\"349\" \/><\/div>\n<\/div>\n<h3>Hooks, Actions, Filters, Hooked Function and Tags<\/h3>\n<p>There is some confusion about these terms above because sometimes they are used interchangeably. I myself mis-speak sometimes, so let&#8217;s get things straight just in case I mess up further down!<\/p>\n<p><strong>Hook<\/strong> is an umbrella term for actions and filters.\u00a0<strong>Actions<\/strong> allow you to add your own functionality next to existing functionality. <strong>Filters<\/strong> allow you to modify existing functionality. Sometimes the word <strong>tag<\/strong> is used to refer to the string that indicates where you are adding your hook. A\u00a0<strong>hooked function<\/strong> is\u00a0a\u00a0function you create\u00a0that\u00a0adds or modifies functionality.<\/p>\n<h3>The Anatomy of a Hook<\/h3>\n<p>When we use hooks we generally rely on two functions: <em><code>add_action()<\/code><\/em> and <em><code>add_filter()<\/code><\/em>. Both these function take four parameters:<\/p>\n<ol>\n<li>The first parameter is the <strong>tag<\/strong>. This is the bit that tells WordPress where to hook your function, when it should be executed.<\/li>\n<li>The second parameter should be the <strong>name of the hooked function.<\/strong><\/li>\n<li>The third parameter is the <strong>priority of the hook<\/strong>. This determines the order in which it is executed if multiple functions are hooked into the same tag.<\/li>\n<li>The fourth parameter <strong>defines the number of parameters passed to this function<\/strong>. By default this is 1, but some tags (like the previous <em><code>wp_insert_post<\/code><\/em>) can have more.<\/li>\n<\/ol>\n<h4>Tag<\/h4>\n<p>The first parameter of both functions is a simple string that states when the hooked function is executed. There are many, many tags you can use. There are action tags that run when posts are deleted, categories are created, a user is logged in and more. Filter tags are run on messages for custom taxonomies, post title displays, dates and more. The <a href=\"http:\/\/codex.wordpress.org\/Plugin_API\/Hooks\" target=\"_blank\">Hooks Reference<\/a> is a good place to start, but sometimes a Google search can be more effective!<\/p>\n<p>Note that some tags may be referred to as <strong>variable hooks<\/strong>. There is a hook named &#8220;<em>{old_status}_to_{new_status}<\/em>&#8221; for example. This hook simply means there are a number of hooks defined for combinations. <em><code>publish_to_trash<\/code><\/em> would run when a publish post is trashed, <em><code>future_to_draft<\/code><\/em> would run when a scheduled post is set to draft status.<\/p>\n<h4>Hooked Function<\/h4>\n<p>Again, a simple string, which should be the exact name of the function. Since this will be a function name it should contain letters, numbers and underscores only.<\/p>\n<p>Take extra care to make this function unique. If you name a function &#8220;send_email&#8221; it may\u00a0clash with someone else&#8217;s function of the same name. It is common practice to prefix function names, something like: <em>my_send_email<\/em>, or <em>wpmudev_send_email<\/em>.<\/p>\n<p>I also advise making your function names readable. You could name your function <em><code>sae()<\/code><\/em>, short for &#8220;send author email&#8221; but this is extremely bad practice as no one else will know what this means, and you will probably forget in a few months as well.<\/p>\n<h4>Hook Priority<\/h4>\n<p>The third function defines the priority of a hook. The lower the priority the sooner the function gets executed. In some cases the order is not important but in some cases it is essential. If you want to send a post publication email to yourself and the author, the order doesn&#8217;t much matter. You can use the default of 10 for both:<\/p>\n<div class=\"gist\" data-gist=\"e5361557ee10610034e5ab136343b388\" data-gist-file=\"multiple-emails.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/e5361557ee10610034e5ab136343b388.js?file=multiple-emails.php\">Loading gist e5361557ee10610034e5ab136343b388<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>However, if you would like to update some statistics whenever a post is updated and then notify the author the order suddenly becomes important:<\/p>\n<div class=\"gist\" data-gist=\"9f2c3824f47ed74e7e4903f64bb119ec\" data-gist-file=\"hook-order.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/9f2c3824f47ed74e7e4903f64bb119ec.js?file=hook-order.php\">Loading gist 9f2c3824f47ed74e7e4903f64bb119ec<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>As you can see, the order in which the <em><code>add_action()<\/code><\/em> calls are places is not important. The second call has a lower priority, therefore it will be executed first.<\/p>\n<p>For filters you need to be more careful. Since filters modify data they may overwrite each other. If two filters are hooked into the same tag, the one with the lower priority is executed first. Then, the one with the higher priority is executed which may change the data completely. Many times this is desirable behavior, it&#8217;s just something to be aware of.<\/p>\n<h4>Parameters<\/h4>\n<p>The fourth parameter sets the number of parameters passed to our hooked function. This is something you will need to hunt down as it depends on what is in the source code.<\/p>\n<p>If you go back to see how we changed the login errors you&#8217;ll notice the following function: <em><code>apply_filters( 'login_errors', $errors )<\/code><\/em>. The first parameter is the tag, the rest are arguments. There is only one here, so one argument is passed to our hooked function.<\/p>\n<p>In case of <em><code>wp_insert_post()<\/code><\/em> you can see the following in the core files: <em><code>do_action( 'wp_insert_post', $post-&gt;ID, $post, true );<\/code><\/em>. The first parameter is the tag, the rest are arguments, so the value here is 3.<\/p>\n<h3>Our Own Hooks<\/h3>\n<p>The great thing about the hooks system is that we can create our own. All we need is the two familiar functions we&#8217;ve seen: <em><code>do_action()<\/code><\/em> and <em><code>apply_filters()<\/code><\/em><\/p>\n<p>Let&#8217;s create our own filter for a plugin which displays products. In the footer we could write: &#8220;Thanks for using our product display plugin,&#8221; which we would allow developers to modify.<\/p>\n<p>Here&#8217;s how that can be done:<\/p>\n<div class=\"gist\" data-gist=\"60329fe336714600f0e7d68f7ae8eaef\" data-gist-file=\"apply-filters.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/60329fe336714600f0e7d68f7ae8eaef.js?file=apply-filters.php\">Loading gist 60329fe336714600f0e7d68f7ae8eaef<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>Actions can be added the same way. You may want to allow users to add some content below the description of a product display:<\/p>\n<div class=\"gist\" data-gist=\"4971d7db22f7af4b09dc47570824631e\" data-gist-file=\"do-action.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/4971d7db22f7af4b09dc47570824631e.js?file=do-action.php\">Loading gist 4971d7db22f7af4b09dc47570824631e<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>I&#8217;ve used the <em><code>do_action()<\/code><\/em> function to allow others to modify my functionality. I&#8217;ve also added the post object as the first parameter, just in case people want to add some post-specific content.<\/p>\n<h3>Conclusion<\/h3>\n<p>In this article we looked at the basics of hooks. We learned the difference between actions and filters and how to use them to effectively change the way WordPress works.<\/p>\n<p>We also had a look at how we can add hooks of our own to our plugins and themes. This is immensely useful for making our work developer friendly.<\/p>\n<p>Hooks are a part of the <a href=\"http:\/\/codex.wordpress.org\/Plugin_API\" target=\"_blank\">Plugin API<\/a>, if you want to learn more about them I suggest reading the relevant pages in the WordPress Codex. The more you use hooks the more you&#8217;ll love them and appreciate the power and flexibility they offer.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you want to get stuck into WordPress development you need to know about hooks, which let you do two things: change default functionality within WordPress, and add your own functionality without modifying core WordPress files.<\/p>\n","protected":false},"author":344049,"featured_media":135957,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"blog_reading_time":"","wds_primary_category":0,"wds_primary_tutorials_categories":0,"footnotes":""},"categories":[263],"tags":[],"tutorials_categories":[],"class_list":["post-135745","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials"],"_links":{"self":[{"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/posts\/135745","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/users\/344049"}],"replies":[{"embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/comments?post=135745"}],"version-history":[{"count":2,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/posts\/135745\/revisions"}],"predecessor-version":[{"id":149967,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/posts\/135745\/revisions\/149967"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/media\/135957"}],"wp:attachment":[{"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/media?parent=135745"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/categories?post=135745"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/tags?post=135745"},{"taxonomy":"tutorials_categories","embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/tutorials_categories?post=135745"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}