In: , , ,
On: 2008 / 07 / 02
Shorter URL for this post: http://ozh.in/ip

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 :)

Shorter URL

Want to share or tweet this post? Please use this short URL: http://ozh.in/ip

Metastuff

This entry "What Plugin Coders Must Know About WordPress 2.6" was posted on 02/07/2008 at 12:10 am and is tagged with , , ,
Watch this discussion : Comments RSS 2.0.

106 Blablas

  1. GaMerZ says:

    Thanks Ozh! You pin pointed my problem!

  2. GaMerZ says:

    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 says:

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

  4. GaMerZ says:

    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 says:

    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 says:

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

  7. GaMerZ says:

    LOL. Great minds think alike =D

  8. Frank says:

    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 says:

    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 says:

    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 says:

    @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 says:

    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 says:

    @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 says:

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

  15. Frank says:

    @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 says:

    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. […] 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. […] 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. […] 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 says:

    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 says:

    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 says:

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

    Should work just fine in 2.6.

  23. Otto says:

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

  24. Ozh says:

    Stephen R » yeah, but, wait!

  25. Ozh says:

    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 says:

    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 says:

    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 says:

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

  29. Jacob Santos says:

    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 says:

    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 says:

    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 says:

    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 says:

    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 says:

    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 says:

    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 says:

    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. […] 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. […] 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 says:

    @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 says:

    Is it alright if I do a

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

    ?

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

  42. […] (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. Ozh,

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

  44. codestyling says:

    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. […] 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 says:

    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. […] 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. 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. oriste says:

    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.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Gravatars: Curious about the little images next to each commenter's name ? Go to Gravatar and sign for a free account
Spam: Various spam plugins may be activated. I'll put pins in a Voodoo doll if you spam me.

Read more ?