Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I'm curious if there is a known issue with WordPress or PHP where the environment may degrade to a point where WordPress simply ignores calls to register_activation_hook(). I was pulling my hair out for an hour on my local environment running MAMP when I had a coworker test the code on her machine; exact same code works on one machine but not the other.

In this case I simply wanted to append messages to the log file which I'm tailing in real-time.

error_log("Plugin code is being processed");

register_activation_hook( __FILE__, 'myplugin_activate' );

function myplugin_activate()
{
    error_log("Attempting to activate");
    die("Should not activate");
}

The output on my machine running MAMP and clicking on the "Activate" link under my plugin:

[22-Sep-2011 22:37:16] Plugin code is being processed
[22-Sep-2011 22:37:16] Plugin code is being processed
share|improve this question

3 Answers 3

I feel a bit dense for not noticing this sooner, but I finally realized that my plugins directory on my computer is a symbolic link to another location as pointed out by Kunal Bhalla in symlinks with WP plugins. Since __FILE__ resolves symbolic links register_activation_hook() is attempting to load a file outside of the WordPress plugins path.

If you look at the register_activation_hook() function in plugin.php you'll see the magic seems to happen in plugin_basename():

function register_activation_hook($file, $function) {
    $file = plugin_basename($file);
    add_action('activate_' . $file, $function);
}

Further examination shows that plugin_basename() is essentially performing pattern replacement in attempt to turn a system path into my_plugin/my_plugin.php; however, since str_replace() is using WP_PLUGIN_DIR as the subject, any system path outside of that the WordPress plugin directory will not produce a match to replace against:

function plugin_basename($file) {
    $file = str_replace('\\','/',$file); // sanitize for Win32 installs
    $file = preg_replace('|/+|','/', $file); // remove any duplicate slash
    $plugin_dir = str_replace('\\','/',WP_PLUGIN_DIR); // sanitize for Win32 installs
    $plugin_dir = preg_replace('|/+|','/', $plugin_dir); // remove any duplicate slash
    $mu_plugin_dir = str_replace('\\','/',WPMU_PLUGIN_DIR); // sanitize for Win32 installs
    $mu_plugin_dir = preg_replace('|/+|','/', $mu_plugin_dir); // remove any duplicate slash
    $file = preg_replace('#^' . preg_quote($plugin_dir, '#') . '/|^' . preg_quote($mu_plugin_dir, '#') . '/#','',$file); // get relative path from plugins dir
    $file = trim($file, '/');
    return $file;
}

This method seems a little short-sited, and I've found several solutions ranging from defining additional constants in wp-config.php to hard-coding the relative path of your plugin. Instead I think a better stop-gap method might be to use the following pattern which pre-defines the path plugin_basename() is attempting to extract. This can be used in place of __FILE__ as the first parameter of register_activation_hook().

basename( dirname( __FILE__ ) ).'/'.basename( __FILE__ );

This method will only work if your plugin has it's own folder in the wp-content/plugins directory and is called from the base plugin file. Both of these conditions seem to be generally in control of the plugin developer, though I'm curious if anyone has some opposing thoughts.


Additional Links:

share|improve this answer
up vote 2 down vote accepted

After a little more thought, I think using a resolved path lookup might be safer:

function plugin_symlink_path( $file )
{
    // If the file is already in the plugin directory we can save processing time.
    if ( preg_match( '/'.preg_quote( WP_PLUGIN_DIR, '/' ).'/i', $file ) ) return $file;

    // Examine each segment of the path in reverse
    foreach ( array_reverse( explode( '/', $file ) ) as $segment )
    {
        // Rebuild the path starting from the WordPress plugin directory
        // until both resolved paths match.

        $path = rtrim($segment .'/'. $path, '/');       

        if ( __FILE__ == realpath( WP_PLUGIN_DIR . '/' . $path ) )
        {
            return WP_PLUGIN_DIR . '/' . $path;
        }
    }

    // If all else fails, return the original path.
    return $file;
}
share|improve this answer

I have used this method: insert the following code in the plugin. THen activate it.

function save_error()
{
   file_put_contents(ABSPATH. 'wp-content/plugins/error_activation.html', ob_get_contents());
}
add_action('activated_plugin','save_error');

after activation, then open yoursite.com/wp-content/plugins/error_activation.html to see what errors happened.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.