3

My plugin uses camelCase in file and folder like

myPlugin/myPlugin.php

I've encountered several issues with this on plugin updates. Most of them work just fine and it's more a cosmetic thing but on some places I just like to go with all lowercase.

What's the best way (or is there any) to update the plugin to

myplugin/myplugin.php

without hazzle the users?

Please note this is a custom plugin and not available on the repo and yes I had to do it lowercase in the first place.

2
  • "I've encountered several issues with this on plugin updates". What issues? I haven't found any issues that causes by file names.
    – Sisir
    Dec 14, 2015 at 20:56
  • WP uses the slug - which is always lowercase - to check if version is up to date, does exits etc. These checks are not required but disturb the UX. I know it will be disabled - which is the only problem - but how can I enable a "foreign" plugin?
    – Xaver
    Dec 14, 2015 at 20:59

2 Answers 2

1

If you just wanted to just change the filename you would have both myPlugin.php and myplugin.php in the plugin and then update the option active_plugins in the database to the new name. Once all of the users have updated you can remove myPlugin.php.

But you want to rename both the folder and filename.

I would get myPlugin/myPlugin.php to install and activate myplugin/myplugin.php. Once myplugin/myplugin.php is installed myplugin/myplugin.php can delete myPlugin/myPlugin.php

You can use TGM Plugin Activation for installing and activating the new plugin. Make to include a check so that the new and old version are not running at the same time.

You can use deactivate_plugins() and delete_plugins() to deactivate and uninstall the plugin.

2
  • Nice approach, thank you. I'm worried about the use of delete_plugins() since it's deletes plugins data with an uninstall.php file. See my answer to this for more info.
    – Xaver
    Dec 15, 2015 at 17:36
  • You could backup the data before deleting the old plugin and then restoring it afterwards.
    – grappler
    Dec 16, 2015 at 8:06
0

Ok, I did some research/testing on it an in short terms:

Don't use uppercase in the first place!

I've currently noticed only one problem where the ajax based update from the plugins overview (introduced in WP 4.2) throws a JS error but it may be a problem in the future.

This is because the ajax response uses sanitize_key in the wp_ajax_update_plugin method (browse in trac).

The sanitize_key method does next to sanitization a strtolower which causes the "myPlugin" gets a "myplugin".

The updateSuccess method tries now to update the row where the slug is "myplugin" instead of "myPlugin".

This could be prevented by changing line 694 in wp-admin/includes/class-wp-plugins-list-table.php from

    printf( "<tr id='%s' class='%s' data-slug='%s'>",
        $id,
        $class,
        $plugin_slug
    );

to

    printf( "<tr id='%s' class='%s' data-slug='%s'>",
        $id,
        $class,
        sanitize_key($plugin_slug)
    );

todo: open ticket Ticket #35032

To answer the initial question

You can change the slug on a plugin update but you have to take care of following:

  • the following code must be included in the old version (eg. `myPlugin/myPlugin.php)
  • the updated version must have a slug of `myplugin/myplugin.php
  • don't use delete_plugin() which may trigger an uninstall.php and removes plugins data

Here's the code you can start with:

//hook into when new plugin has been successfully updated
add_filter( 'upgrader_post_install', 'my_move_plugin_slug', 10 ,3 );

function my_move_plugin_slug($should_be_true, $hook_extra, $result){

    global $wpdb, $wp_filesystem;

    $from = 'myPlugin/myPlugin.php';
    $to = 'myplugin/myplugin.php';

    //some general checks
    if(!$should_be_true || !isset($hook_extra['plugin']) || $hook_extra['plugin'] != $to) return $should_be_true;

    $old_destination = $result['local_destination'].'/'.dirname($from).'/';

    //old location doesn't exist (anymore)
    if(!is_dir($old_destination)) return $should_be_true;
    //new location is the same as the old one
    if($old_destination == $result['destination']) return $should_be_true;

    //do the magic

    //rewrite location in the database
    echo '<p>Moving the plugin to new location… ';
    if(false !== $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET `option_value` = replace(option_value, %s, %s)", $from, $to))){
        echo '<strong>done</strong>';
    }
    echo '</p>';

    //delete folder
    echo '<p>Removing the old directory of the plugin… ';
    if($wp_filesystem->delete( $old_destination , true )){
        echo '<strong>done</strong>';
    }
    echo '</p>';

}

Second method

If you have a dedicate batch update progress in you plugin like I have you can use this code which is in my opinion a bit safer as it's triggered by explicit user interaction and doesn't require wp_filesystem:

function do_change_plugin_slug(){

    global $wpdb;

    $from = 'myMail/myMail.php';
    $to = 'mymail/mymail.php';

    $old_destination = WP_PLUGIN_DIR.'/'.$from;
    $new_destination = WP_PLUGIN_DIR.'/'.$to;

    //old location doesn't exist (anymore)
    if(!file_exists($old_destination)) return true;
    //new location is the same as the old one
    if($old_destination == $new_destination) return true;

    //do the magic

    echo 'Removing the old file of the plugin… ';
    if(rename( $old_destination , dirname($old_destination).'/'.basename($new_destination) )){
        echo 'done';
    }else{
        echo 'failed';
    }
    echo "\n";

    echo 'Removing the old directory of the plugin… ';
    if(rename( dirname($old_destination) , dirname($new_destination) )){
        echo 'done';
    }else{
        echo 'failed';
    }
    echo "\n";

    //rewrite location in the database
    echo 'Moving the plugin to new location… ';
    if(false !== $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET `option_value` = replace(option_value, %s, %s)", $from, $to))){
        echo 'done';
    }else{
        echo 'failed';
    }
    echo "\n";

    return true;

}

In the end you should always make sure the user understands what's going on and/or why this is a necessary step.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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