« WordPress Settings API Tutorial
First Post »

Don’t include wp-load, please.

February 9, 2010, 4:22 pm

Originally published here: ottodestruct.com/blog/2010/dont-include-wp-load-please/

Note: There is a followup post to this one, which details an even better way than the two given below: ottopress.com/2010/passing-parameters-from-php-to-javascripts-in-plugins/

Time for Otto’s general griping: WordPress plugin programming edition.

Here’s a practice I see in plugins far too often:

  1. Plugin.php file adds something like this to the wp_head:
    <script src="/img/spacer.gif"> 
    
  2. Script.js.php has code like the following:
    <?php
    include "../../../wp-load.php";
    ?>
    ... javascript code ...
    

The reason for this sort of thing is that there’s some option or code or something that the javascript needs from the database or from WordPress or whatever. This PHP file is, basically, generating the javascript on the fly.

Usually, the case for this turns out to be something minor. The code needs the value from an option, or some flag to turn it on or off. Or whatever.

Problem is that finding wp-load.php can be a bit of a chore. I’ve seen extreme efforts to find and load that file in plugins before, including searching for it, examining the directory structure to make decent guesses, etc. This sort of thing has existed even before wp-load.php came around, with people trying to load wp-config.php themselves and such.

But the real problem is simpler: This is always the wrong way to do it.

Why this is wrong

  1. You don’t have the first clue where wp-load.php actually is. Both the plugin directory and the wp-content directory can be moved around in the installation. ALL the WordPress files could be moved about in this manner, are you going to search around for them?
  2. You’ve instantly doubled the load on that server. WordPress and the PHP processing of it all now have to get loaded twice for every page load. Once to produce the page, and then again to produce your generated javascript.
  3. You’re generating javascript on the fly. That’s simply crap for caching and speed and such.

The right way? Well, there’s two options.

Right Way the First

Generate your options separately, put them in using wp_print_scripts.

Examine this pseudo-code:

add_action('wp_print_scripts','myscript');
function myscript() {
?>
<script type="text/javascript">
  var plugin_option= <?php echo json_encode(get_option('plugin_option')); ?>;
</script>
<?php
}
wp_enqueue_script('myscript','...myscript.js',...);

Basically, the plugin option value is inserted directly into the page. The myscript.js file that loads shortly afterwards can use this value however it likes.

Why this is better:

  1. No searching for wp-load.php.
  2. The javascript is static, only your options are variable. No added load on the site.
  3. Static scripts mean you get great speed from caching.

Sidenote: Note the use of json_encode? You should always use this when producing javascript variables from PHP variables. It handles quoting and escaping and everything for you. It can even turn PHP arrays into javascript arrays nicely! Handy for storing all your options in one place.

What if you have a ton of plugin options though? What if you really WANT to generate that javascript on-the-fly?

Right Way the Second

Generate the javascript from a call to WordPress itself, not to a separate file.

Examine this pseudo-code:

add_filter('query_vars','plugin_add_trigger');
function plugin_add_trigger($vars) {
    $vars[] = 'plugin_trigger';
    return $vars;
}

add_action('template_redirect', 'plugin_trigger_check');
function plugin_trigger_check() {
	if(intval(get_query_var('plugin_trigger')) == 1) {
	?>
function javascript_code() {
...
}
<?php
	exit;
	}
}

That code does something a little clever. Basically it’s adding a new query variable to be used as the “trigger”. When the trigger gets pulled, a bunch of javascript is generated, then the code *exits*, stopping WordPress from proceeding any further.

So, with that code in a plugin, a call to example.com/?plugin_trigger=1 will now produce your javascript code. This is running entirely within the content of a WordPress call, so you get all the WP functions and database access with which you can generate your code as well.

Thus, you can happily put your

<script src="/img/spacer.gif"> 

code into the page and it’ll load up that “file” just fine.

Why this is better:

  1. No searching for load.php
  2. … Well, okay, there is no other reason. The load problem still exists, and your caching issues still exist. You’re programmatically generating code here, after all. It’s not a particularly good practice to do. Still, sometimes this is easier and faster to develop, even if it’s never actually “necessary”.

Also see that while I’m using the number 1 there as the value I’m checking for, that value can be anything you like. If you want to be a smartass about it and have all sorts of different things that could be generated, you can do them all with that one trigger. It’s still a generally bad idea because of the added load, but hey, maybe you have a legitimate reason. I’ve seen one or two valid reasons to do this before.

Wrap up

Also note all of the above also applies to “generating” CSS code. It’s just as unnecessary. Usually more so.

So please, stop including wp-load. It’s just wrong. Let WordPress load itself, and make your plugin do the output it needs to produce in the right places.

Shortlink:
Tags: PHP, plugin, wp-config, wp-load
Category: Code, Rants, WordPress  |  Comment (RSS)  |  Trackback

51 Comments

  1. […] include wp-load, please. Edit: This post has moved to here: ottopress.com/2010/dont-include-wp-load-please/. Take your comments […]

    Reply to this comment
  2. Hey Otto, I’ve come with a idea that may help in a lot of cases.

    It’s for when we wanna build a dynamic CSS or JS, and during the page building we already know the data we wanna use. Generally that happens when the CSS/JS just needs to use a get_option() to load some data from database, which we can access in page-loading time, or when in page-loading time we generate something we wanna pass to it.

    Anyway, if before we add the or potentially print the whole code in HTML document, we already know the info that code will need, we can pass it as GET parameters to the file. In the href attribute, after the file URL, we add a ‘?’ and just pass our data as key=value, as we normally do when we manually build a GET parameters string, using urlencode () and so forth.

    Then, when the dynamic CSS/JS is loaded, we just use $_GET[] to get those data back and deal with default values when a parameter is not present. I’ve tested it with CSS for a plugin I’m developing and it worked beautifully, in admin page I allow user to set config parameters and when the file must me used I pass them as GET parameters.

    For values that are arrays, what I’ve thought is use serialize() and unserialize(), but didn’t use in practice.

    And there’s another issue, if we use wp_enqueue_style(), we can pass our prepared string to it as if it was the file version, and WordPress appends our string, but before it appends a “?ver=”, which would turn our first parameter key to the “ver” parameter value, and break this first parameter. In this case, I just start the string with “0=dunny”, which results in “ver=0=dummy&firstparameter=…”, works great!

    I believe this solution can solve most cases where wp-load is required only to read data, any idea on how to enhance it would be very appretiated spacer

    In a few weeks I’m gonna publish my plugin, then you can see it working in practice.

    Reply to this comment
    • Your solution still requires a second, and wholly unnecessary, round trip to the server just to produce your code.

      It’s really a lot better to insert the code into the page directly. It’s faster and it requires less server hits.

      Reply to this comment
      • Yeah, as any js and css does spacer

        Beyond all advantages of separating HTML, JavaScript and CSS, all this is to “access” WordPress when dynamically generating these files. And it doesn’t require the use of wp-load, which is much faster already.

        Reply to this comment
  3. Hello Otto,
    nice post – a ask, do you have an example on a sorce-file for real work example. I dont understand your tutorial not perfectly. Thanks for reply.

    Reply to this comment
    • It’s hard to give an example without knowing exactly what it is that you’re trying to accomplish, basically. There’s a lot of ways to do it, but including wp-load from a plugin is always the wrong way.

      Reply to this comment
  4. Thanks for you reply!
    I will include a own javascript-file. In this js-file it is important to use the path of WP, get_options(‘wpurl’) as example and i will not use the wp-load.php in this js.file, as php-file and send header as javascript. YOu has examples of code hier, but this are foo examples and i will read this on a plugin or script for WP, on a real example. Maybe i understand your solution better. Thanks for read my bad english and also thanks for your time.

    Reply to this comment
    • Okay, so if there’s only some small piece of information you need, then really, what you should do is to include that information onto the main HTML of the page itself as a javascript variable, then have your script use that variable when it gets included.

      “Right Way the First” above basically demonstrates exactly how to do this in a plugin.

      In other words, you’re basically saying that you want to make a PHP file which generates javascript code. That’s a BAD way to do things. Instead, make the javascript code static, such that it doesn’t have to be generated but can merely be loaded directly. The pieces that you would have needed to generate before, rewrite them to use variable names. Then have code in your plugin that sets those variables on your main site itself.

      Does that help?

      Reply to this comment
      • Yes, i think so and i have an idea for my work.
        I will give the the path on a variable for the javascript and use this on my js. The js i will include with wp_enqueue_script().
        Many thanks, i will test this and the change all my sources with wp-load.php.

        I have read in so much plugins and all go the way via hook wp_head or wirte the source via echo in the pages. Small one of plugins use wp_enqueue_script() – i think the right way, and i hove no find a plugin without wp-load.php for values from WP, example the path. I think, it is important, that more people write plugins and posts for dont use wp-load.php.

        Reply to this comment
  5. […] over to Otto’s blog and read his post entitled don’t include wp-load, please which I’m in complete and total agreement with. I’d like to add to the solutions with […]

    Reply to this comment
  6. Hi Otto,

    (here is it reformatted)

    Nice post, thanks.

    Question though: @Ozh mentioned in comments on your other site to use URL parameters to which you said “this still causes caching problems.” In looking at it it doesn’t seem to be the case so I’m asking to see what I am missing. (I’m assuming that the generated Javascript is a function of the URL parameters and nothing more.) Let me give a simply example to allow us something to discuss:

    script.js.php?foo=1&bar=2

    if script.js.php contains the following (which is mindlessly simplistic on purpose to allow us to discuss):

    <?php
    // script.js.php
    var test = json_encode(array(
    ‘foo’ => $_GET[‘foo’],
    ‘bar’ => $_GET[‘bar’],
    ));
    ?>

    Doesn’t every permutation of ‘foo’ and ‘bar’ cache correctly; i.e. for any given URL you will always get exactly the same Javascript, no?

    Or were you referring to the fact that is we have 10 different ‘foo’s and 10 different ‘bar’s we could end up with 100 different URLs each of which would need to be downloaded and cached separately?

    If I am not missing something it would seem then that using URL parameters would not be so much a “caching problem” but instead a working solution that is poorly optimized for HTTP caching, correct? (Note the following script would cause a what I would term to be a “caching problem.”)

    <?php
    // script.js.php
    var test = time();
    ?>

    Again, I’m just trying to see if I am missing anything. Thanks in advance.

    -Mike

    Reply to this comment
    • There’s a few problems with it.
      1. Sometimes browsers don’t like to cache things with ?foo=bar and such in them. Not every browser is Firefox or Chrome.
      2. As you stated, if you have a lot of possible inputs, then you’ll have a lot of possible resulting scripts. This is a problem considering that we’re talking about caching by individual browsers here.

      Basically, creating GET parameters like that make things not work as well or as beautifully and they should be avoided. Sometimes they’re acceptable when you’re in a hurry or just don’t really care, but ideally I prefer my code to be more elegant.

      BTW, if you’re using wp_enqueue_script for this sort of thing, WordPress has a function designed to do this the right way called wp_localize_script. It was originally designed for extending I18N translations into javascript files, but it works equally well for parameter passing to static JS files. It’s definitely the preferred way to go when the alternative is having PHP code that dynamically builds Javascript code…

      To use it, you just enqueue your STATIC javascript as per normal:

      wp_enqueue_script(‘whatever’,’/path/to/whatever.js’,…);

      Then you call localize script with an array of your parameters:

      $myparams = array(‘foo’=>’bar’, ‘setting’=>123);
      wp_localize_script(‘whatever’,’ParamName’, $myparams);

      What this will do is make WordPress output an inline script with your parameters (properly json encoded) just before the output of the script tag to load your javascript. Then those parameters will be available to your javascript as an instance of an object with “ParamName” (from my example). So your static script can reference them as “ParamName.foo” and “ParamName.setting” and so forth.

      Much cleaner. One static and unchanging JS file to cache. Parameters get put into your HTML itself as a one-liner. You can deal with the parameters using a normal PHP array before passing them over… Perfect, basically.

      Reply to this comment
      • Thanks for the follow up. I do agree that using the approach #1 you describe in the post and in this comment are far more elegant and preferred approaches. OTOH are you sure that there are browsers that have more than 0.1% market share that don’t cache correctly? That’s a pretty fundamental and low-level aspect of a reasonable HTTP implementation.

        And thanks for the wp_localize_script(); although actually I learned of this post because of @Frank’s reference to your post on the (currently private and pre-beta) WordPress Answers at StackExchange (it’ll be public by August 19th, 2010.) @Frank was answering a question on the subject of this post and gave the use of wp_localize_script() as his answer. I have been wondering if there was a better solution than what I had been doing (i.e. your #1 in this post) and was very happy to see Frank’s answer.

        Reply to this comment
  7. […] while back, I wrote a post detailing why it was a bad idea to generate Javascripts using PHP in WordPress plugins. In that post, I […]

    Reply to this comment
  8. Otto, you rock it!!! i thoght same about wp_head and i came here searching for a solution with a plugin that uses this and once you use a child theme it break code…. solved and thanks!

    Reply to this comment
  9. I’ve been using the Right Way the Second for quite awhile to build replies to front end AJAX requests. (Thanks!) I build my version of example.com/?plugin_trigger=1 like this:

    $ajax_url = bloginfo( 'url' ) . '?plugin_trigger=1';

    That’s been working for nearly everyone, but I have one report from someone with a server configuration that requires PATHINFO permalinks reporting that it won’t work without adding index.php before the querystring. Is there a more generic “template redirect URL” I should be using?

    Reply to this comment