Reader Odai left a great question in his comment today on our plugin tutorial:

Looking at the code for The Complete Plugin, do you need to have a closing ?> for the opening <?php?

Or is it okay to just end with a }?

I learned the answer to this a long time ago, so long that I no longer think to mention it; thanks to Odai for asking so we could write about it.

An Ending for Every Beginning?

For those of us who like order it seems like an opening PHP tag <?php should always be matched to an closing tag ?> but in the case of PHP tags in WordPress plugins the truth is you are asking for trouble if you use the closing tag. But why?

PHP is for Logic Embedded in Content

PHP is an embeddable scripting language designed to allow you to embed logic in the form of PHP scripting into content, which is typically HTML. In that context the opening and closing tags make sense, such as in WordPress' theme template files. Consider the Twenty Twelve theme's page.php file sans comment header:

  1. <?php get_header(); ?>
  3.   <div id="primary" class="site-content">
  4.     <div id="content" role="main">
  6.       <?php while ( have_posts() ) : the_post(); ?>
  7.         <?php get_template_part( 'content', 'page' ); ?>
  8.         <?php comments_template( '', true ); ?>
  9.       <?php endwhile; // end of the loop. ?>
  11.     </div><!-- #content -->
  12.  </div><!-- #primary -->
  14. <?php get_sidebar(); ?>
  15. <?php get_footer(); ?>
<?php get_header(); ?>

  <div id="primary" class="site-content">
    <div id="content" role="main">

      <?php while ( have_posts() ) : the_post(); ?>
        <?php get_template_part( 'content', 'page' ); ?>
        <?php comments_template( '', true ); ?>
      <?php endwhile; // end of the loop. ?>

    </div><!-- #content -->
  </div><!-- #primary -->

<?php get_sidebar(); ?>
<?php get_footer(); ?>

Except When It's Not

But in plugins you don't want embedded content[^except], you only want programming code. Consider the simplest possible plugin we could envision, it disables the status bar that logged-in users would otherwise see on the front end:

The Problem is Trailing White Space

Now let's assume we added the closing ?> to this file and let's add some trailing white space such as the newlines on lines 9 through 12:

Extra Newlines after PHP Closing Tag
Extra Newlines after PHP Closing Tag

1. HTTP Headers Already Sent

The first problem with the extra white space is the "unexpected output" error you get. What unexpected output you ask? The three (3) newlines after the closing ?> tag:

Unexpected Output Sent Before HTTP Headers
Unexpected Output Sent Before HTTP Headers

Why this happens is that the HTTP protocol first sends HTTP headers after which it then sends the content. If any "content" is sent first before new HTTP headers are sent the browser doesn't know how to respond. Since plugins are loaded early in a page load WordPress has yet to send all the headers it might send and in cases like plugin activation you get errors.

2. Broken AJAX Requests

White Space after Closing PHP Tag Breaks AJAX Calls
White Space after Closing PHP Tag Breaks AJAX Calls

The second problem is if you are DOING_AJAX. AJAX requests expect a rigid response format, typically either JSON, XML or in WordPress sometimes Serialized PHP, and they do not work if stray characters make their way into the response.

In the screen capture to the right you can see the error message when trying to add a Category from within the post editor screen while our "@Disable the Admin Bar" plugin activated.

And in the screen capture below here's what it looks like in HTTPScoop which is the HTTP protocol sniffer[^http-sniffer] we use for debugging:

HTTP Response Errors with Extra Whitespace in HTTP Scoop
HTTP Response Errors with Extra Whitespace using HTTPScoop

Again, for Themes?

Trailing whitespace after a closing ?> tag are not a problem with theme template files. Themes template files are always loaded after all HTTP headers have been sent, or at least they should be so this is a moot point for themes.

PhpStorm Knows All

Don't believe us? Take a look at what HardcoreWP's secret weapon PhpStorm has to say on the subject:

Redundant PHP Closing Tag as Show in PhpStorm
Redundant PHP Closing Tag as Show in PhpStorm

But, What if I'm Careful?

Yes if you are careful you can use closing PHP tags ?> in your WordPress plugins, nothing will break and it will all be good. But why? Why risk the potential that you might inadvertently add trailing whitespace without thinking, or worse that your editor, version control system or some other tool might add trailing whitespace without you even realizing it? There's zero benefit to include a closing tag and only potential downsides, so why do it?

We think the answer is clear.


And it's always nice to get validation. 🙂

[^except]: With the obvious exception being when plugins have their own special template files and/or when they contribute template files or template fragments to be used in combination with the theme.
[^http-sniffer]: We use HTTPScoop on the Mac for US$15 although there are several others. There's Charles for US$35 which has more features but is more complicated to use, and for Windows there is Microsoft's free and excellent Fiddler2. I wish there was something as good as Fiddler2 for Mac OS X; almost makes me want to switch back to Windows...

28 thoughts on “Always Omit Closing PHP Tags in WordPress Plugins

  1. WOW! Thank you, Mike, thank you! 😀

    I’m actually working on a plugin that ends with a closing tag. It’s a widget, so if I understand, the closing tag isn’t causing problems because it hooks in after the header info is sent? Either way, I’m going to remove it if it’s not needed.

    • @Odai – You are welcome. Your questions made for a good post, and lots of retweets; had no idea it would resonate so much more than my other posts have.

      Also, it doesn’t matter if the plugin is a widget or not, it is still loaded early by WordPress so the problems would appear just the same. So it’s good that you are removing them.

  2. Thanks Mike, a great explanation as always.

    I’m curious, is there any advantage that HTTPScoop has over the Network Tab in Chrome Dev Tools, which also shows header information? Only one I can think of is that HTTPScoop isn’t browser dependant.

    Also, whenever I read your praise of PHPStorm I am tempted to swap to it from my current editor, which is BBEdit, as it just doesn’t have similar advanced debugging tools for PHP. Maybe a demonstration of hardcore PHPStorm with WordPress could be an idea for a post here?

    • Hi @David,

      Thanks for your comment.

      Ah yes, Chrome Dev Tools will work too, if that’s all you got. And some people may even prefer it, maybe you will too. But with a 14-day free trial might as well try HTTPScoop yourself (note: no affiliate link there, I’m just a user that thinks it’s a useful tool.)

      Why is HTTPScoop better than Chrome Dev Tools for me? Because with Browser-based tools you have too much going on in the same program and can easily often loose track of what you were debugging. I’d rather have a standalone app off to the side that is recording my HTTP traffic and not have to worry about changing windows in the browser and loosing track of my recent traces. But don’t get me wrong, HTTPScoop has a lot it could do to improve, it’s just the best I have found so far for Mac. It’s much like why I prefer Navicat for MySQL over phpMyAdmin.

      And yes PhpStorm is amazing, however it’s not a perfect fit for everyone though. Some people just really like other editors such as Sublime. And I’ve heard users of those programs say that they don’t need debuggers. More power to them if true. OTOH I’ve also heard a lot of them ask questions on forums that I can find the answer to myself in 5 minutes when I use PhpStorm + Zend debugger.

      But even for those who buy into PhpStorm it does require a lot of effort to configure; getting the debugger to work can be very frustrating. And then getting the keys mapped in a way that doesn’t drive you crazy takes a while too. But once you get it configured it’s, as I said, amazing.

      As for a demonstration of PhpStorm; yes, I plan a whole series of posts about tools. But I have to do some research as I want to make sure I cover the important points and because I want to include screencasts so I need to research the best tools for that. So hopefully within 12 months but I doubt I’ll get to it for at least the next 3; too much other stuff to do and to write about.

  3. Pingback: Why you shouldn't use closing PHP tags in WordPress plugins : Post Status

  4. I notice in the TwentyTwelve theme that the functions.php file doesn’t have a closing PHP tag.

    Does this article apply to a theme’s functions.php file as well, in that it shouldn’t have a closing PHP tag either?

    • Hi @David,

      Great question. If you trace through how the code loads using a debugger like PhpStorm + Zend Debugger you’ll see that functions.php is loaded shortly after plugins are loaded and both are loaded by the wp-settings.php file. So yes, functions.php in a theme follows the same rules as plugins for the omit tag.

      One way to think about it is that plugins are for theme independent extension code (PHP/JS/CSS) and functions.php is for theme dependent extension code as most everything you can do in a plugin you can do in functions.php and vice-versa.

  5. It’s not only true for WordPress but for all PHP apps. The header issue is really hard to debug and the closing tag is a very usual problem. AFAIK, this is also recommended in PSR – PHP Standard Recommendations.

    • Hi @Tran,

      Absolutely, it’s true for PHP too and it is a very annoying problem and is a frustrating one that can waste so much time to debug.

      And you are right about the PSR, it’s PSR-2 Coding Standards section 2.2 General/Files.

      A note about this blog though, we’re focusing on WordPress-specific issues so we don’t planning to elaborate in our posts about how the topics relate to things on a broader scale than WordPress. Even more, we plan at least one post where we try to make the point that general PHP best practices are not necessarily best practices for PHP in WordPress.

      As just one example, I don’t think following the PSR-0 directory structures is a good idea when working with WordPress. Another is that PSR-2 requires a style of opening brace on a new line for classes and functions and WordPress standards imply they should start on the same line so lots of WordPress plugin code follows that latter convention.

      BTW, long time no talk. Hope you are doing well!

      • Thanks Mike for pointing the differences between PSR & WordPress Coding Standards. Actually, I said that because it has a good reason for why people should do that. I prefer WordPress Coding Standards, but still look at PSR to know why it’s like that. Listening to other opinions is also good, though.

        • Hi @Tran,

          Yes, of course. I definitely know you’ve got the knowledge and experience to differentiate between the two. I was replying to your comment for future visitors who might not know that WordPress standards and PSRs can conflict.

          Again, thanks for visiting and hope to see you back again as I publish more.

  6. Because of some trailing lines I once wasted about 2 hours trying to troubleshoot the problem. When I realized what was wrong, I wanted to punch something…
    Now I know how to avoid this 🙂

    • Hey @John,

      Thanks for the comment!

      You know we should get together sometime given we are both in Atlanta. Maybe I could come see that wonderful new office of your sometime and we could do lunch?

  7. Pingback: Always Omit Closing PHP Tags in WordPress Plugins | Chris Wallace

  8. This is good to know, and I would have never thought this construct was true! At the very least it is not evident. Thanks for the tip.

  9. Nothing worse, then spending a few hours trying to find a problem, only to find that its a silly extra line behind the closing tag. Especially frustrating with Ajax calls.


Leave a reply

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code lang=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" extra="">