To __FILE__ or not to __FILE__

…well that IS the question today.

A Little Background

__FILE__ is a magic constant in PHP. When it is invoked in a PHP script, it returns the fully qualified, fully resolved, path to the current file. What could be easier? It is built into PHP, the popular general-purpose scripting language that is especially suited to web development.

WordPress, written in PHP, is a fully extensible, open-source, content management system for developing web content. WordPress, licensed under the GPLv2, supports extensibility through the use of custom themes to change its appearance and drive its user interface and custom plugins to add functionality. WordPress is used by more than 33% of all active websites on the planet.

I write plugins for WordPress.

WordPress Structure – Simple View

Generally, WordPress websites have the following structure.

/
	wp-admin
	wp-content/
		plugins/
			abusive-ip-blocker/
				classes/
				core-mods/
				css/
				js/
				lib/
			child-themes-helper/
			plugin-starter-kit/
		themes/
			twentynineteen/
			twentyseventeen/
			twentysixteen/
			twentytwenty/
		uploads/
	wp-includes
index.php
wp-activate.php
wp-config.php
wp-cron.php
wp-login.php
wp-settings.php
... and more files ...

The structure of each plugin or theme is at the discretion of the theme or plugin’s developer. For my plugins, such as the ‘abusive-ip-blocker’ shown above, I use subfolders for PHP classes, stylesheets, and javascript scripts.

The ‘wp-admin’ folder and the ‘wp-includes’ folders have WordPress core files. These files should never be directly edited or modified in any way by website owners or theme developers or plugin developers. Through automatic updates, any changes made to the WordPress core will get unapologetically overwritten with every update.

WordPress Structure – Complex View

WordPress is very flexible. You can move your folders around. You can put them wherever you want and just add a constant to the wp-config.php file to tell WordPress where to find them.

Most modern operating systems, including Microsoft Windows, Apache, Nginx, and others, support symbolic links. This lets a website owner put their files and folders in one place on the webserver or on the network and create a link to that file or folder within the WordPress website structure.

Let’s Create an Example

I develop my WordPress plugins on a Microsoft Windows 10 computer. My development folders reside in the OneDrive structure so all of my changes are pushed to the cloud automatically. But the test website, built using Local by Flywheel’s development tool, resides elsewhere.

For this example, I will use the Abusive IP Blocker plugin. It is my newest and most extensive plugin. My plan is to release a free version to the WordPress Plugin Repository before the coronavirus stay-at-home order is lifted and then create a premium version that can be my full-time job starting next year.

The path below is where the website resides, by default, for a site named ‘aib-test’ in Local by Flywheel.

c:/users/<username>/local sites/aib-test/app/public

The WordPress plugins, for the AIB-Test site, reside here:

c:/users/<username>/local sites/aib-test/app/public/wp-content/plugins

My development folder for the AIB plugin is here:

d:/onedrive – paulswarthout.com/development/plugins/abusive-ip-blocker

I use a symbolic link in the AIB-Test site to point at my folders. For Microsoft Windows, you use the ‘mklink’ command on the command line to create a symbolic link. For Linux-flavored operating systems you would use the ‘ln’ command.

For Windows, I open a command prompt and type:

c:
cd \users\<username>\local sites\aib-test\app\public\wp-content\plugins
mklink /J 
    abusive-ip-blocker 
    'd:\onedrive - paulswarthout.com\development\plugins\abusive-ip-blocker'
exit
mklink note

The mklink command is shown, above, on separate lines above. You should enter it as a single command. I put it on different lines so it would not wrap at an odd place and make it less readable.

Additionally, you should replace <username> with your login username.

Therefore, the website’s path to the Abusive IP Blocker’s main file is:

/wp-content/plugins/abusive-ip-blocker/abusive-ip-blocker.php

To __FILE__ or not to __FILE__

As a reminder, the PHP magic constant __FILE__ returns the fully qualified, fully resolved, path to the currently executing script’s file.

There are two views of this file.

  1. The view of the file as the webserver ‘sees’ it.
  2. The view of the file as the operating system ‘sees’ it.

In all cases, with the exception of symbolic links, #1 and #2 are the same. For symbolic links, #1 and #2 are different.

The PHP magic constant __FILE__ always returns #2. And this is by design. I find this definition to be silly, at best.

Here we have a scripting language, designed for creating web content, that has a built-in feature to return the path to a file, under the control of the webserver, and it ‘chooses’ to return how the operating system ‘sees’ the file, not how the webserver ‘sees’ it. And PHP offers no alternatives to accomplish the same thing and return it as #1.

In my plugin, the Abusive IP Blocker, I have written a function, which is called when the plugin loads initially, which determines #1 above. I use that value throughout the plugin, instead of __FILE__.

The Bottom Line

Whether you can safely use __FILE__ with symbolically linked folders, is largely dependent upon how your plugin works and what resources it needs.

The Abusive IP Blocker has a feature where it will export a list of blacklisted IP addresses to a file on the webserver. I choose to write that file into a subfolder of the wp-content folder instead of the plugin folder so the website owner doesn’t have to search for the file.

What is the Problem?

The real problem is that when I’m looking at the root folder of my plugin

/wp-content/plugins/abusive-ip-blocker

when my plugin is symbolically linked, all attempts to open a file with the following:

$file_handle = fopen( dirname( __FILE__ ) . ‘/../../aib_export_file.txt’, “w+” );

will fail because while you think you are trying to open this file:

c:/users/<username>/local sites/aib-test/app/public/wp-content/aib_export_file.txt

The webserver is instead attempting to open this file:

d:/onedrive – paulswarthout.com/development/aib_export_file.txt

which the webserver does not have permissions to access. In fact, you will repeatedly get error messages indicating access denied while attempting to open the file.

Note

This only happens when you call __FILE__ from a script file that is in a symbolically linked folder and you’re attempting to access a file that is NOT in the current folder or its subfolders.

Calling __FILE__ from the abusive-ip-blocker.php file, when the plugin folder is symbolically linked will return the following:

d:\onedrive – paulswarthout.com\development\plugins\abusive-ip-blocker\abusive-ip-blocker.php

Executing the same script, when the folder is NOT symbolically linked will return the following:

c:\users\<username>\local sites\app\public\wp-content\plugins\abusive-ip-blocker\abusive-ip-blocker.php

In my example. This is by design, by those who developed and continue to develop the PHP scripting language. It is up to the plugin and/or theme developer to know about this and write their code accordingly.

For the Abusive IP Blocker, I choose to build my own webserver-centric path to my plugin from the $_SERVER[‘DOCUMENT_ROOT’] and use that instead of __FILE__ because I always know that it is right.

Conclusion

There are a lot of things that I really like about PHP. There are a few things that I don’t like about it. But among my most hated pet peeves of a development language are things that force a developer to develop workarounds to language limitations. This is one of those limitations. And because it is by design, it’s not likely to be changed anytime soon.

© 2020, PaulSwarthout. All rights reserved.