[wp-trac] [WordPress Trac] #22802: Empower Plugin Developers to make Symlink Compatible Plugins
WordPress Trac
noreply at wordpress.org
Fri Dec 7 05:43:27 UTC 2012
#22802: Empower Plugin Developers to make Symlink Compatible Plugins
------------------------------------------------+--------------------------
Reporter: MikeSchinkel | Type: defect (bug)
Status: new | Priority: normal
Milestone: Awaiting Review | Component: Plugins
Version: 3.4.2 | Severity: normal
Keywords: has-patch dev-feedback 2nd-opinion |
------------------------------------------------+--------------------------
Currently it's effectively impossible to use symlinked plugins
'''''reliably''''' in a WordPress installation. For those not familiar
with the problems with symlinks, let's assume we have a structure like the
following where two sites are on the same server, and then there is a
plugins directory that is on peer with the site directories:
{{{
/home/myacct/mysite1/ <-- Site #1 goes here
/home/myacct/mysite2/ <-- Site #2 goes here
/home/myacct/plugins/ <-- All the shared plugins go here
}}}
Now let's assume one of those plugins use `plugins_url( 'js/myajax.js',
__FILE__ )` to get a URL for this external file. But the problem is that
PHP resolves symlinks before returning the value of `__FILE__` but it does
not provide any way to translate the value from __FILE__ back into the
symlinked virtual directory. So when you need a URL that looks like this:
{{{
http://mysite1.com/wp-content/plugins/myplugin/js/myajax.js
}}}
You instead get this URL because of the symlink:
{{{
http://mysite1.com/wp-
content/plugins/home/myacct/plugins/myplugin/js/myajax.js
}}}
This problem has been discussed on tickets #16953 and #13550 and in both
cases the discussion apparently stalled because there wasn't a good viable
way to automatically support plugins that have been symlinked into the
expected directories.
But what CAN be done is for the plugin developer to be proactive and
handle write their plugin to be symlinkable.
[http://alexking.org/blog/2011/12/15/wordpress-plugins-and-symlinks Alex
King proposed a solution] as
[http://wordpress.stackexchange.com/questions/15202/plugins-in-symlinked-
directories/15204#15204 did Jan Fabry] which captures the file path value
from the global variables `$plugin`, `$mu_plugin` or `$network_plugin`,
whichever is applicable.
Unfortunately that works in many cases, but not all. Specifically those
fail during plugin activation and plugin deactivation as you can see in
[https://gist.github.com/4230921 the code I've extracted from a working
class file]. This class file shows how frought with peril it is to try to
make this work in all applicable use-cases ''(For those interested
[https://github.com/newclarity/sidecar/blob/master/classes/class-
base.php#L148 here is the real file] that uses this code from its GitHub
repo.)''
Forunately there is a very simple modification to WordPress core that will
empower plugin developers to make their plugins symlinkable by simply
replacing `__FILE__` with `$GLOBALS['wp_plugin_file']`; easy peasy. Rather
than WordPress core call `include_once()` every place where it needs to
load a plugin WordPress core could call `wp_load_plugin()` instead. Here's
how simple `wp_load_plugin()` can be:
{{{
function wp_load_plugin( $plugin_file ) {
global $wp_plugin_file;
include_once( $wp_plugin_file = $plugin_file );
unset( $wp_plugin_file );
}
}}}
Simple, bulletproof, and here's [https://gist.github.com/4230972 what
plugin code could look like] with this change (compared to
[https://gist.github.com/4230921 this].)
Note I decided to use a ''(disappearing)'' global variable because that
would be the most performant method of capturing the plugin file value vs.
assigning to a static object properties or similar, and because I think it
will be the easiest syntax for people to use, i.e.:
{{{
global wp_plugin_file;
$url = plugins_url( 'js/myajax.js', $wp_plugin_file );
}}}
And being ''"disappearing"'' the code releases `$wp_plugin_file` from
memory immediately after loading the plugin so chance of future conflict
with anything else in WordPress is extremely unlikely.
I've attached a patch with includes this `wp_load_plugin()` function. The
patch also calls `wp_load_plugin()` in all the places I've identified that
load plugins. The version in the patch is actually a bit more complicated;
it has logic to calculate and assign a `$wp_plugin_slug` too, because the
plugins slug is needed at times and it would be really convenient if it
could be made available during plugin load too ''(but of course adding
`$wp_plugin_slug` is not nearly as important as adding `$wp_plugin_file`):
{{{
function wp_load_plugin( $plugin_file, $plugin_type = 'plugin' ) {
global $wp_plugin_file, $wp_plugin_type, $wp_plugin_slug;
$wp_plugin_type = $plugin_type;
$dir = preg_quote( 'plugin' == $plugin_type ? WP_PLUGIN_DIR :
WPMU_PLUGIN_DIR );
$wp_plugin_slug = preg_replace( "#^{$dir}/(.+)$#", '$1', str_replace(
'\\', '/', $plugin_file ) );
include_once( $wp_plugin_file = $plugin_file );
unset( $wp_plugin_file, $wp_plugin_type, $wp_plugin_slug );
}
}}}
In summary this addresses symlinking by enabling a new best practice that,
when following, would empower plugin developers to build symlinkable
plugins without going to extremely fragile lengths and that would work
reliably in all cases.
BTW, if you've never had this problem try using some of your own plugins
in a symlinked directory first before jumping to conclusions about what's
required; it wasn't obvious to me how difficult this problem is to address
until after I needed to.
--
Ticket URL: <http://core.trac.wordpress.org/ticket/22802>
WordPress Trac <http://core.trac.wordpress.org/>
WordPress blogging software
More information about the wp-trac
mailing list