What Plugin Coders Must Know About WordPress 2.6

A WordPress install is a bunch of directories and files, but two of them are particular: the file wp-config.php and the directory wp-content/ are personal and don't get overwritten when you upgrade your blog. In WordPress 2.6, they get so personal that you can even move them out of the WordPress root. This must bring a major change to your coding habits.

Plugin coders sometimes need their script to guess the location of their own directory (for example to require() files), or to include wp-config.php to make a file live alone in a WordPressized environment.

Guessing the path of wp-content

WordPress 2.6 allows advanced users to specify the location (physical and URL) of this directory with a constant define, so the directory might not be where it used to be.

What you used to do:

  1. $plugin_path = ABSPATH . '/wp-content/plugins/' . plugin_basename(dirname(__FILE__));
  2. $plugin_url = get_option('siteurl') . '/wp-content/plugins/' . plugin_basename(dirname(__FILE__));

What you will have to do, now that this directory can hide anywhere:

  1. // Pre-2.6 compatibility
  2. if ( !defined('WP_CONTENT_URL') )
  3.     define( 'WP_CONTENT_URL', get_option('siteurl') . '/wp-content');
  4. if ( !defined('WP_CONTENT_DIR') )
  5.     define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' );
  6.  
  7. // Guess the location
  8. $plugin_path = WP_CONTENT_DIR.'/plugins/'.plugin_basename(dirname(__FILE__));
  9. $plugin_url = WP_CONTENT_URL.'/plugins/'.plugin_basename(dirname(__FILE__));

In WordPress 2.6, constants WP_CONTENT_DIR and WP_CONTENT_URL are either user defined or set in wp-settings.php

Including wp-config.php

In WordPress 2.6 you can either leave wp-config.php in the blog root directory, or move it to the parent folder (which makes sense if this takes this critical file off of the webserver's document root)

What you used to do:

  1. require_once('../../../wp-config.php');

What you must do now:

  1. $root = dirname(dirname(dirname(dirname(__FILE__))));
  2. if (file_exists($root.'/wp-load.php')) {
  3.     // WP 2.6
  4.     require_once($root.'/wp-load.php');
  5. } else {
  6.     // Before 2.6
  7.     require_once($root.'/wp-config.php');
  8. }

Basically in WordPress 2.6 the file responsible for loading the environment is, in first instance, wp-load.php in place of the good old wp-config.php.

However, as pointed out by GamerZ in the comments, this still may not work. Why? Because not only the config file may not be there anymore, but the relative path to it may have changed since wp-content might have been moved too.

At this point though, there is no way I can think of to guess the locations of both wp-config and wp-content. To be 100% foolproof, such a "standalone" file needing to include wp-config should be editable so that advanced users moving their wp-content directory could manually edit a location path (the $root variable in the previous example)

Summary

Coders, revisit your old plugins to make sure they won't eventually break on WordPress 2.6 :)

106 comments

  1. GaMerZ

    Thanks Ozh! You pin pointed my problem!

  2. GaMerZ

    With regards to loading the wp-config.php, if the /wp-content/ is in custom location. I am thinking that

    1. $root = dirname(dirname(dirname(dirname(__FILE__))));

    May not work.

  3. Ozh

    GaMerZ » You're totally fucking right of course :) I'm updating the article

  4. GaMerZ

    Thanks Ozh! That is why I am in such a dilemma. I think in worst case scenario, I shall ask the user to manually enter in the path if he place the /wp-content/ in custom location. I can't seem to find a better way than this.

  5. Peter

    Thanks for this! This should be bookmarked by all plugin developers! Now I just need to get around to proactively updating my plugins using your handy code :P

  6. Ozh

    GaMerZ » I came to same conclusion while updating the article :)

  7. GaMerZ

    LOL. Great minds think alike =D

  8. Frank

    The third problem is the load og language-file: my solution:

    1. if (function_exists('load_plugin_textdomain')) {
    2.     if ( !defined('WP_PLUGIN_DIR') ) {
    3.         load_plugin_textdomain('adminimize', str_replace( ABSPATH, '', dirname(__FILE__) ) . '/languages');
    4.     } else {
    5.         load_plugin_textdomain('adminimize', false, dirname(plugin_basename(__FILE__)) . '/languages');
    6.     }
    7. }
  9. Frank

    and my solution for the plugin-path, works nice:

    1. $my_path = get_option( 'siteurl' ) . '/' . PLUGINDIR . '/' . plugin_basename( dirname(__FILE__) ) . '/folder_in_plugin_folder/';
  10. GaMerZ

    Some Useful constants as well

    1. if ( !defined('WP_PLUGIN_DIR') )
    2.     define( 'WP_PLUGIN_DIR', WP_CONTENT_DIR . '/plugins' );
    3. if ( !defined('WP_PLUGIN_URL') )
    4.     define( 'WP_PLUGIN_URL', WP_CONTENT_URL . '/plugins' );
  11. GaMerZ

    @Frank You can just use this in WP2.6:

    1. add_action('init', 'useronline_textdomain');
    2. function useronline_textdomain() {
    3.     load_plugin_textdomain('wp-useronline', false, 'wp-useronline');
    4. }

    Where wp-useronline is the plugin folder name.

  12. Ozh

    Frank » Your $my_path way of getting things is perfect for 2.5, but it won't work in 2.6 if the wp-content has moved.

  13. Frank

    @GaMerZ: yes, you can us the init-Hook. I think it is better to ask for defined the constants WP_PLUGIN_DIR and then works alo in WP < 2.6 and 2.6

    1. function my_textdomain() {
    2.  
    3.     if (function_exists('load_plugin_textdomain')) {
    4.         if ( !defined('WP_PLUGIN_DIR') ) {
    5.             load_plugin_textdomain('my_plugin', str_replace( ABSPATH, '', dirname(__FILE__) ) . '/languages');
    6.         } else {
    7.             load_plugin_textdomain('my_plugin', false, dirname(plugin_basename(__FILE__)) . '/languages');
    8.         }
    9.     }
    10. }
    11.  
    12. add_action('init', 'my_textdomain');
  14. GaMerZ

    Ops my bad, yea I just realize that is 2.6 only =D

  15. Frank

    @ozh: yes, sorry. The new constants is WP_PLUGIN_DIR in 2.6, you can ask for defined and change the constants.

    1. if ( !defined('WP_PLUGIN_DIR') ) {
    2.     $my_path = get_option( 'siteurl' ) . '/' . PLUGINDIR . '/' . plugin_basename( dirname(__FILE__) ) . '/folder_in_plugin_folder/';
    3. } else {
    4.     $my_path = get_option( 'siteurl' ) . '/' . WP_PLUGIN_DIR . '/' . plugin_basename( dirname(__FILE__) ) . '/folder_in_plugin_folder/';
    5. }

    or you leave this on WP, is defined in settings.php

    1. /**
    2.  * Allows for the plugins directory to be moved from the default location.
    3.  *
    4.  * @since 2.6
    5.  */
    6. if ( !defined('WP_PLUGIN_DIR') )
    7.     define( 'WP_PLUGIN_DIR', WP_CONTENT_DIR . '/plugins' ); // full path, no trailing slash
    8. if ( !defined('WP_PLUGIN_URL') )
    9.     define( 'WP_PLUGIN_URL', WP_CONTENT_URL . '/plugins' ); // full url, no trailing slash
    10. if ( !defined('PLUGINDIR') )
    11.     define( 'PLUGINDIR', 'wp-content/plugins' ); // Relative to ABSPATH.  For back compat.
  16. GaMerZ

    I found another issue, you can't use

    1. !defined('WP_PLUGIN_DIR')

    to check for 2.6 as we may define it in order for it to maintain backward compatibility with WP2.5
    I think

    1. if (!function_exists('wp_print_styles'))

    would be a better choice.

  17. Lester Chan’s WordPress Plugins » Blog Archive » My Plugins With WordPress 2.6 Part 2

    […] good friend Ozh has written an article entitled, What Plugin Coders Must Know About WordPress 2.6. It is a good read if you are a plugin developer for […]

  18. Weblog Tools Collection » Blog Archive » Plugin Coders and WordPress 2.6

    […] WordPress. Go ahead, subscribe to our feed! You can also receive updates from this blog via email.What Plugin Coders Must Know About WordPress 2.6: Just like it sounds, Ozh has started a post, followed by a lively comment thread, on the […]

  19. WordPress 2.6 Development Notes – Funroe Factory

    […] Okay, time to start some collections of notes for WordPress 2.6.First, covering the new self discovery of WordPress Plugin directories and wp-config locations: What Plugin Coders Must Know About WordPress 2.6 « planetOzh […]

  20. Stephen R

    Thanks for this Ozh. I'm definitely going to need this. :)

    Could you maybe do a "clean" update post after the dust settles from all the "but wait!"s in comments?

  21. Stephen R

    Actually, whoever put this change into WordPress really should have anticipated all this and created a simple, standardized method for getting the right paths.

    Sometimes the core WP coders get ahead of themselves, at the expense of the community and the platform itself. :\

  22. Otto

    Maybe I'm missing something, but:
    require_once(ABSPATH.'wp-load.php');

    Should work just fine in 2.6.

  23. Otto

    Doh, never mind. I see the problem with that. Silly me.

  24. Ozh

    Stephen R » yeah, but, wait!

  25. Ozh

    Franck :

    1. if ( !defined('WP_PLUGIN_DIR') ) {
    2.    $my_path = get_option( 'siteurl' ) . '/' . PLUGINDIR . '/' . plugin_basename( dirname(__FILE__) ) . '/folder_in_plugin_folder/';
    3. } else {
    4.    $my_path = get_option( 'siteurl' ) . '/' . WP_PLUGIN_DIR . '/' . plugin_basename( dirname(__FILE__) ) . '/folder_in_plugin_folder/';
    5. }

    Again, no, this won't work. You can change the wp-content directory to put it anywhere on the server, like have site.com/wordpress and site.com/wp-content. So getting its path relative to get_option( 'siteurl' ) won't always work. That's why there is WP_CONTENT_URL as well as WP_CONTENT_DIR.

  26. Otto

    Stephen R: First off, it's highly unusual for a plugin to need to load the wp-config manually anyway. If it does need to do that, then it's probably written wrong, and could do what it needs in some other way.

    A plugin that really needed to include wp-load (because it is being called externally to WP itself and hooking on a custom GET variable or something is not an option for some reason) could always do the following:

    1. On activation, save the ABSPATH value into a file somewhere.
    2. Read that file, use it as the include path for wp-load.

    Not the best approach, but it would work. And quite frankly, a plugin should almost never need this information. Ever.

  27. Ozh

    Otto » I made several plugins where I needed to include wp-config (ex: Absolute Comments, where a standalone file is used to display the latest comment via a javascript call). This said, writing the info in a file (upon activation and other events as well) is a good idea

  28. Dan Coulter

    Great post! I'll have to bookmark this and visit all of my plugins.

  29. Jacob Santos

    I would advised against calling dirname() so many times. Function calls have additional overhead each time they are called, even if the contents are cached. The function stack still needs to be created. For this purpose, it is more efficient to use require('../../../../file.php');.

    Given that most plugins won't be running for that long of a time, I believe calling multiple dirname()s won't matter. It shouldn't be advised to get in the habit of doing so.

  30. Otto

    Ozh: No, your plugins only had to include wp-config.php because a) you had a file that was called directly and b) you didn't go through the normal WordPress index.php to get to that part of your plugin.

    But it is perfectly possible for a WordPress plugin to intercept a call to WordPress's main index.php by adding, say, a ?plugin=do_something to the main site URL, and then doing its own output (ajax stuffs) and then calling "exit();" to prematurely stop WordPress from doing any output of its own. If you had done it THAT way, you would not need to include wp-config directly, because you'd already be going through WordPress to get the stuff you needed.

  31. Otto

    Nothing like a demonstration to explain what I mean…

    1. class Demo {
    2.     // Make a new query variable
    3.     function vars($public_query_vars) {
    4.         $public_query_vars[] = 'demo';
    5.         return $public_query_vars;
    6.     }
    7.  
    8.     // Intercept "?demo=1" links
    9.     function output_demo() {
    10.         if(intval(get_query_var('demo')) == 1) {
    11.             // do something to create my output here.. could be anything, even an include
    12.             echo "This is a demo";
    13.             exit;
    14.         }
    15.     }
    16. } // class Demo
    17.  
    18. // install the hooks
    19. add_filter('query_vars', array('Demo', 'vars'));
    20. add_action('template_redirect', array('Demo', 'output_demo'));

    See? You can make any sort of output you want, if you're creative about it.

  32. Ozh

    Jacob Santos » I think there can be some inconsistencies with ../../../.. depending on the environment. I remember someone talking about this on wp-hackers, just don't remember the context (chrooted? nfs? stuff like this)

  33. Ozh

    Otto » It's when loading files directly, yes. I remember having troubles with this method in the past. If I remember, the difference between including wp-config and simply going via index.php is that you dont generate the same headers and this was causing me some troubles with output buffering (don't remember specifically but I did lose some hair at this time)

    This said, for most cases it may indeed be simpler.

  34. PJ Brunet

    I realize this was sortof a security bonus, but I'm wondering if this was a step towards making it easier for multiple blogs to share the same wp-config file? Or was that not even a concern?

    Not that it really matters to me because I hacked wp-config to auto-config itself ;-)

    1. $table_prefix = substr(basename(dirname(dirname(__FILE__))), 0, 4) . '_';

    FYI: This code works w/ my host, your host may have a different directory structure! Basically this sets the MySQL table_prefix to the first four characters of the domain name.

  35. Ozh

    PJ Brunet » It might make things easier, although I really doubt this was in mind when implemented. Anyway, you don't really need this sort of feature, you could just have a rather blank wp-config file for each blog that would just require() a unique central file.

  36. Jacob Santos

    Ozh: If you talking about safe mode, then the way to get around it is by calling dirname(__FILE__) once then use the relative paths. You can further extend the concept by using realpath(), which will get the absolute path from the relative one.

    I do remember that discussion, but I don't agree with the commenter. It isn't clean code and while it has no probable consequences, I think it promotes bad coding practices for those who don't know better. Those that do know better are okay.

    I still have to contend with developers putting functions inside of other functions, which is more terrible. I think if that one guy hadn't done it, then the widget development would be better off. Explaining why it is such a bad idea is along the same lines, performance, except that there is a possibility of bugs occurring from the practice.

  37. wp-fun » WordPress f**** with its best feature

    […] In WordPress 2.6 a lot of plugins could fail if they are not updated. The changes are explained by Ozh in What plugin coders know about WordPress 2.6. […]

  38. Wordpress 2.6 y los desarrolladores de plugins | aNieto2K

    […] escribe un artículo en el que nos muestra los cambios más significativos de la nueva versión de WordPress, la 2.6, en relación a los desarrolladores de […]

  39. GaMerZ

    @Otto I have plugins that makes use of including wp-config.php. By pointing to WordPress index page and then exiting the page do not work for. I tested it and it works for $_GET but $_POST seems to have a problem.

  40. GaMerZ

    Is it alright if I do a

    1. if (file_exists('../../../wp-load.php')) {

    ?

  41. links for 2008-07-03 | ????? ?? ??”? ???

    […] What Plugin Coders Must Know About WordPress 2.6 « planetOzh ?? ?? ?????? ?????? ?????? ???? ?? ??????? 2.6 ?? ??? ????? ??? ?????? ???? ????? ????? ?? ????? ????? – ???? ????? (tags: wordpress 2.6 plugins compatibility) […]

  42. WP 2.6 beta 2 and Princess Quin | Pinoy Teens Online

    […] (if not all) to work the way you don't want them to, or not seeing them work at all. A topic has been raised about this issue and I find it very vital to share this to you, so that you will become aware of some changes that […]

  43. Stephen Cronin

    Ozh,

    Thanks for the heads up – and the solution. This will affect most of my plugins…

  44. codestyling

    A question about images served out of directories directly now based out of external direct access scope:
    does WP use a new php file to redirect any image attempt out of a plugin directory not longer accessable by uri directly ?
    If so, the server load will increase, cause at cgi environments an additional cgi must be launched to deliver the image instead of Apache direct file delivery.
    How this will be handled ?

  45. GigPress › GigPress 1.3.3

    […] Just committed version 1.3.3 of GigPress, fixing a couple of bugs, and adding compatibility with the forthcoming WordPress 2.3 (as tipped off by Ozh.) […]

  46. BillH

    Why guess? For the absolute path to the current file you can add the following line:

    1. $my_path = dirname(__FILE__);

    And for the URL of the current file, you can use the following line:

    1. $my_uri = dirname("http://" . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF']);

    Using these lines in your function and accessing these variables,it doesn't really matter where your function resides on your server. I've used both successfully in PHP programming as well as functions I've written for WordPress.

  47. Important changes in WordPress 2.6 | gaarai.com

    […] 2.6 allows for moving the wp-content folder and the wp-config.php file. Some code guidelines have been published to help developers standardize their plugins with the new changes. The following code will allow […]

  48. Jennifer Hodgdon

    Hi Ozh,

    Thanks for this! There is one more thing I ran into, which is that if your plugin is internationalized, when you load_plugin_text_domain, you need to add a new argument to the end to deal with this possible new location of wp-content.

    So if you used to do:

    load_plugin_text_domain( 'mypluginname', 'wp-content/plugins/myplugindir');

    you now need to do:

    load_plugin_text_domain( 'mypluginname', 'wp-content/plugins/myplugindir', 'myplugindir');

    This second line is compatible with both the old and new ways of doing things.

    Cheers,
    Jennifer

  49. AWSOM.org = Artist Website Setup Options Markup » Blog Archive » WordPress 2.6, more updates more (possible) Plugin problems

    […] UPDATE: Here's a link to a good write up on the changes Plugin writers need to know to be compatible with the new 2.6 version–> http://planetozh.com/blog/2008/07/what-plugin-coders-must-know-about-wordpress-26/ […]

  50. oriste

    Note to self (I'm not a plugin developer): leave everything in its default place and start using subversion asap for future update-ease-of-use. Thanks for the clear explanation though.

Comments are closed.