﻿id,summary,reporter,owner,description,type,status,priority,milestone,component,version,severity,resolution,keywords,cc
22802,Empower Plugin Developers to make Symlink Compatible Plugins,MikeSchinkel,,"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.",enhancement,closed,normal,,Plugins,,normal,wontfix,has-patch dev-feedback 2nd-opinion,mike@… pippin@… frank@… junk@… info@… kenn@… boonebgorges@… naomicbush goldenapplesdesign@… wordpress@… mindctrl
