Here are two sample scripts along with an API to provide automatic updates for plugins and themes you host on your own server.

Inside /api you’ll find index.php which processes all the update requests. You should place this in something like http://updates.example.com and update $api_url in /plugin/test-plugin-update/test-plugin-update.php and /theme/portfolio-racer/inc/updates.php accordingly. If you activate these sample plugins without changing API URL, updates will be checked against my test server. If you decide to update, both plugin and theme will be replaced with exactly the same version of each.

Use Contact Form 7 to collect business leads and enquiries? I created Storage for Contact Form 7 plugin which stores them safely in WordPress database.

38 Comments

Hey there,
Quite the awesome idea but I can’t seem to get it to work when I change the API url to the correct path on my own server.

I did some investigation and found that if I print_r($raw_response); after line 44 in the test-plugin-update.php file, which is the activated plugin, I’m getting an error on line 41 of the index.php file in the api folder. The error is as follows:

[body] =>
Warning: array_shift() [function.array-shift]: The argument should be an array in /home/ivannova/public_html/demo/wpdev/api/index.php on line 41
O:8:"stdClass":1:{s:4:"slug";N;}

Ivan, I really can’t debug it without direct access to the code. If you are going to implement this into any of your themes/plugins, this might be a good opportunity to take the time and learn how the update procedure works.

Kaspars, thanks for your assistance in getting me on the right path to solving the problem. It is truly appreciated. The problem ended up being that the wp_remote_post(); function actually adds slashes within the $_POST['request'] serialized array. A simple addition of stripslashes() around $_POST['reqeust'] around line 36 does the trick, like so:

Does the api index.php file work by informing WordPress that an update is necessary if the date OR the version are different (or both different)? And does it make a difference what the actual date is of the new version (zip file), i.e. is it just the date specified in the index.php api page that rules whether an update is necessary.

Also, if I wanted to update just by version number and date, is this easy to do?

So the actual date of the Plugin/theme zip file, and date entered into the $packages array, never cause an update to occur? And it is purely the version number entered in the api index.php that triggers the update?

If I wanted to trigger an update do I just change the version number in index.php, and that’s it?

Correct, David — the date is used only for refference, so that both you and the user know when the update was released. In the API index.php you see a version comparison, which triggers the actual update:

yep I enabled that. I’ve tried on my local host and on a remote server. I even tried just uploading the unmodified racer theme to one of my sites and it still didn’t do anything even with forced requests on :P

I’m currently using the code in both a theme and a plugin with great success.

I’m working on a new plugin that will auto-update, and it’s raised one question – do you know what I need to add to the $package returned by the API in order to change the message inside WordPress > Updates from “Compatibility with WordPress 3.0.5: Unknown” to “Compatibility with WordPress 3.0.5: 100% (according to its author)”??

My plugin is confirmed to work up through the latest development trunk of WP, and it’s a shame to see the Update page say that compatibility is “Unknown”

Important: if you want to use this system to update multiple plugins (on the same environment) … the “plugins_api” filter should never at any point return “false”. Instead, based off of the args it receives it needs to lookup and return a result.

Additionally consider using a hash/assoc-array when calling the wp_remote_post() function … something like the following, this will ensure that a slug is directly tied to a api_url.

$request = wp_remote_post($hosts[$args->slug], $request_string);

The plugins_api has a weird implementation, see /wp-admin/includes/plugin-install.php … plugins_api() function … note how when the filters are applied, it needs to return a result. Having an implementation which returns false some of the time and a result other times causes problems here.

I would like to protect the package url. Now it is a direct link to a zip file but I would like to point to a download.php file. So, my question is, how the file content (of the zip file) has to be served from the download.php file that wordpress can install it?

Daniel, here is how I would do it — store all your client domain names and use them as identifiers (set as an HTTP referrer) when an update request is made. Once the request hits your download.php, simply check if that domain is in your database. Yes, this approach is not 100% reliable as that header can be spoofed, but it’s a very simple and fast way of doing it.

Amazing script. Thanks Kaspars. I got it working with some struggle. First I was not getting the slug on my server, so I had to hard-code it in the script. Then I changed the packages array to add lot more information so that it feels like the update is coming from the WordPress server (now it also generates that Details pop-up when a user clicks the link – get version x.x details).

Only thing I’m not able to figure out is the Upgrade Notice. Is there a way to get that working?

Kaspars,
I really appreciate you sharing such a great solution. I’m having an issue with the versioning of my theme. No matter what version I’ve identified in the styles.css, the script always displays that there is an upgrade, even if the theme is a newer version that identified in the api/index.php. Not sure what I’m doing wrong.

Weemo, I created this script just as a proof of concept because there were no alternatives out there at the time. Therefore, I suggest that in a production environment you use this Auto Update class by Jānis Elsts instead.

Hello Kapars,
Even after 5 years this script works like charm.
but i got a question i protected archive folders with basic access authentication with htaceess. i do have stored logins in my .htpasswd. and i am able to get update details from secured folder by adding this header in wp_remote_post