Efficient PHP: Don’t Abuse dirname( __FILE__ )

Every now and then I run across a chunk of PHP code at the top of a file that looks something like this:

[sourcecode lang=”php”]
require dirname( __FILE__ ) . ‘/path/to/something.php’;
require dirname( __FILE__ ) . ‘/path/to/another.php’;
require dirname( __FILE__ ) . ‘/path/to/me-too.php’;
require dirname( __FILE__ ) . ‘/path/to/sure-why-not.php’;
require dirname( __FILE__ ) . ‘/path/to/kitchen-sink.php’;
[/sourcecode]

and what jumps out at me is the repeated use of dirname( __FILE__ ) for each require statement (for now we’ll avoid asking why anyone would need to include the kitchen-sink in their code base). My gut instinct is to call dirname( __FILE__ ) once, save that in a variable and then reference the variable to build the path. Not wanting to go on instinct alone I put together a small test to see if it really would make any difference.

The contrived test code will compare approach A:

[sourcecode lang=”php”]
$var = dirname( __FILE__ ) . ‘/path/to/something.php’;
$var = dirname( __FILE__ ) . ‘/path/to/another.php’;
$var = dirname( __FILE__ ) . ‘/path/to/me-too.php’;
$var = dirname( __FILE__ ) . ‘/path/to/sure-why-not.php’;
$var = dirname( __FILE__ ) . ‘/path/to/kitchen-sink.php’;
[/sourcecode]

with approach B:

[sourcecode lang=”php”]
$path = dirname( __FILE__ );

$var = $path . ‘/path/to/something.php’;
$var = $path . ‘/path/to/another.php’;
$var = $path . ‘/path/to/me-too.php’;
$var = $path . ‘/path/to/sure-why-not.php’;
$var = $path . ‘/path/to/kitchen-sink.php’;
[/sourcecode]

I’m not testing with require in an effort to focus just on the difference repeated dirname( __FILE__ ) calls make, not how fast the filesystem can slurp in PHP libraries.

VLD

My first test was to pass each approach through VLD to see how much “work” PHP was doing. For that I pulled out the number of operations needed for each approach:

approach A: 37 ops
approach B: 23 ops

Calling dirname( __FILE__ ) once required 37% fewer operations. This is a bit of blunt measurement since it doesn’t attempt to give individual weights to the different operations, but it gives a good general view. The rule of thumb is that fewer ops is better than more ops.

Time

I tried running each approach in a simple loop, but it always ran so quickly that I didn’t see any useful difference.

Memory

Next up was a look at memory_get_peak_usage. Turns out there was a small difference:

approach A: 57,312 bytes
approach B: 56,992 bytes

Sure 320 bytes isn’t a big deal in the world of servers with 16GB of memory, but it’s one more reason why approach B is just that tiny bit better.

Conclusion

If you see this pattern creeping into your code base and you can easily convert it then you’ll likely be better off for it. Remember that everything is a trade off though, if it takes you several hours to go through and make this kind of change it may not be worth it.

I’d certainly file this away for new projects though so that you can avoid repetitive dirname( __FILE__ ) calls from the start. For that matter, if you can get away with running on 5.3 or higher then you’d probably want to skip this entirely and look at using the new __DIR__ constant. I haven’t tested how it compares to the two approaches listed above, but I’d expect it to be at least as good as approach B, perhaps even better.

For reference I tested this using PHP 5.2.10-2ubuntu6.4 with Suhosin-Patch 0.9.7 (cli) (built: Jan 6 2010 22:41:56). Your specific version of PHP may behave differently.

12 Comments

  1. As you say there are trade offs. Your solution requires just a little more cognitive burden unless a nice tight bit of code, and so smells like an early optimization to me in some cases.

    For WordPress, I don’t get why people do require dirname( __FILE__ )… . It’s effectively a relative path written to be PHP CLP friendly, and definitely makes sense when packaging up other PHP projects. But in WordPress you already have nice WP_CONTENT_DIR, and related constants, as well as numerous dynamic path providing functions.

  2. The WordPress case is a bit different, and unfortunately not always clear cut. While constants like WP_CONTENT_DIR to basically the same thing as my approach B, a plugin doesn’t always have enough information to make correct use of the constant.

    For instance, how does a WordPress plugin know if he is in WP_CONTENT_DIR . ‘/plugins/my-plugin-name/’ or in WP_CONTENT_DIR . ‘/mu-plugins/my-plugin-name’?

  3. This was an eye opener! I use dirname() and basename() a lot. I wonder if this is also a performance issue when using an accelerator such as APC with PHP.

    Luckily in my case for many of my applications, I get the root folder with a special function call so my quick plan is to set a unique global variable when the function is called the first time and all future calls return the global variable. When I do a major rewrite I will change my projects to a unique define approach. I’m definitely going to switch my WordPress plugins to use a define for the root folder as well!

  4. Right, I think the general goal for something like this is to minimize the number of times something like dirname( __FILE__ ) is called. And if you can avoid it entirely with __DIR__ in 5.3 then all the better!

  5. don’t forget you can use the current directory indicator (the dot) in required and include.

  6. The general rule is to use full paths when ever possible.

  7. Using full paths is often not possible, e.g. developing for a client with a different directory layout to what you’re developing with locally.

    Or, when the client changes the paths of your files.

  8. dirname( __FILE__ ) gets you the full path, no matter where your file lives or gets moved to.

  9. Have you tested this with __DIR__ ? (it’s a new magic constant as of PHP 5.3.0)

  10. I haven’t tested it, but I’d expect it to be at least as efficient as calling dirname( __FILE__ ) just once, if not more so.

  11. Hi

    I’m not expert in PHP, but I see a problem in your approach or I am doing something wrong! 🙂

    If in all the files I define
    $dirname=dirname(__FILE__)
    this will modify the value of that variable in the including files (is there any way to have a really local variable?)

    Suppose the following files:
    master.php includes dir1/child.php and dir2/child.php and the child.php files inlude other files.

    [master.php]

    [child.php]

    In this situation, I will get an error in master.php when trying to include
    require_once($dirBase . ‘/dir2/child.php’);
    because after including ‘dir1/child.php’ the value of dirBase will have changed!

    But If I use dirname(__FILE__ everywhere (unfortunately I can’t use __DIR__) everything works fine!

    As I have said, probably I’m doing something wrong, any hint please? 🙂

    Thanks

    Isi

  12. I have justed played around with YiiFramework and tested it.

    A page from yii-user-management module (index page) took 3-4 seconds to load.

    I analyzed it and saw YiiFramework uses dirname all over the place. 99% of the total page loading time was is the dirname method!

    Why is everyone using YiiFramework I wonder…

Leave a Reply

Your email address will not be published. Required fields are marked *

© 2019 Joseph Scott

Theme by Anders NorénUp ↑