Posted on by

Finding the WordPress Root Path for an Alternate Directory Structure

In authoring a plugin, I’ve found that it can be difficult to reliably find the path to the WordPress install root. The convention is to use the WordPress constant ABSPATH, and that works for most installations, but increasingly, people are using alternate directory structures. That is helpful for development, but can make things tricky for a plugin developer to create a plugin that will work in such an environment.

In the admin, there is a convenient function get_home_path(), but sometimes you need to know the root path in the frontend, and I’ve also found that get_home_path() isn’t available early in the cycle, so you can run into fatal errors trying to call the function before it’s been defined. There is no point in risking that so I needed to find a more reliable alternative.

Why is the Path Needed? Won’t the URL do?

If you’re just going to display an image or include a script or stylesheet, you just need the URL to the resource. That’s easily found using functions such as home_url() or plugins_url(). In my Participants Database plugin, forms can include files and images, and since the whole premise of the plugin is that it is somewhat separate from the data that WordPress stores for posts and such, it uses it’s own uploads directory.

That location is outside of the plugin install (it would have to be, right?) so when the plugin is installed, the directory has to be created. We also give users the ability to delete their uploaded files and images. For both of these cases, a URI won’t do, you need to work with the system path to the file or directory.

Alternate Directory Structures in WordPress

It’s common in WordPress development to work from a repository where the WordPress application (the stuff you download from WordPress.org) is separate from all the stuff the developer is actually working on. For instance, instead of the default directory structure shown here (this diagram doesn’t include everything you’d normally find…it’s abbreviated to make my point):

wp-content/
   plugins/
   themes/
   uploads/
wp-admin/
wp-includes/
.htaccess
wp-config.php
.
.
. (etc...)

Your root directory could look like this:

content/
   plugins/
   themes/
   uploads/
wp/
   wp-admin/
   wp-content/
      plugins/
      themes/
      uploads/
   wp-includes/
.htaccess
wp-config.php
.
.
. (etc...)

With this kind of setup, all of the WordPress download (or fresh from the repo) is in the wp/ directory. The content/ directory is where all the coding happens. In a setup like this, ABSPATH ends up being something like /home/user/public_html/wp/ which is not a great starting point for getting to anything in the content directory.

The Solution I Use

OK, so ABSPATH can’t get me to the content directory, but WP_CONTENT_DIR can, so why don’t I just use that? The simple answer is that I set up my plugin to give the user the ability to store the plugin uploads in any location on the WP install…and that includes outside of the content directory, so I needed a way to find the base install path regardless of the name and location of the content directory or the WordPress application.

I didn’t have to do it that way, but I made that choice long ago and I can’t change it without breaking installations that are already out there…and we try to avoid that.

The solution I use is to combine the two constants and find the path in common to both: this should get me to the root path to the WordPress install. Here is the function I use:

function xnau_wp_app_base_path() {
   // separate out the path components
   $content_path = explode('/', WP_CONTENT_DIR);
   $wp_app_path = explode('/', ABSPATH);
   // find the length of the shortest one
   $end = min(count($content_path), count($wp_app_path));
   $i = 0;
   $common = array();
   // add the component if they are the same in both paths
   while ($content_path[$i] === $wp_app_path[$i] and $i < $end) {
      $common[] = $content_path[$i];
      $i++;
   }
   // I like it to have a slash at the end, but you may have a different style
   return trailingslashit(implode('/', $common));
}

I essentially use this function in place of every use of ABSPATH, except in those rare cases where I need to access a file (such as an include) in the WP install. In a standard install, it will be identical to ABSPATH, but for those installations where things are set up differently, the pathing is now consistent and reliable.

If anyone has a reason why I shouldn’t do this or can’t rely on it, I’d love to hear about it.

Leave a Reply
You have to agree to the comment policy.

Leave a Reply
You have to agree to the comment policy.