Skip to content

Instantly share code, notes, and snippets.

@mikeschinkel
Created February 21, 2012 22:14
Show Gist options
  • Save mikeschinkel/1879381 to your computer and use it in GitHub Desktop.
Save mikeschinkel/1879381 to your computer and use it in GitHub Desktop.
WordPress plugin to download list of WordPress' plugins into the WordPress database (written pre-Custom Post Types)
<?php
/*
Plugin Name: Sync Plugin Info
Plugin URI: http://mikeschinkel.com/wordpress-plugins/sync-plugin-info
Description: Synchronizes Plugin Info from <a href="http://api.wordpress.org" target="_blank">api.wordpress.org</a> to tables in the local MySQL database
Version: 0.1
Author: Mike Schinkel
Author URI: http://mikeschinkel.com
*/
//global $sync_plugin_info;
//$sync_plugin_info =
$sync_plugin_info = new SyncPluginInfo(true);
//while ($sync_plugin_info->sync()!=0);
class SyncPluginInfo {
const PLUGINS_TABLE_NAME = 'extend_plugins';
const DB_VERSION = '0.1';
const PLUGIN_KEY = 'sync-plugin-info';
const NEXT_PLUGIN_KEY = 'sync-plugin-info_next-plugin';
const DB_VERSION_KEY = 'sync-plugin-info_db-version';
const SCHEDULED_TASK = 'sync-plugin-info_hourly-sync';
const LAST_BATCH_FILE = 'sync-plugin-info_last-batch.txt';
const LAST_BATCH_SQL = 'sync-plugin-info_last-batch.sql';
const PLUGIN_API_URL = 'http://api.wordpress.org/plugins/info/1.0/';
const BATCH_SIZE = 50;
const DEBUG = true;
private $debug;
static $singleton;
function SyncPluginInfo($debug=false) {
$singleton = $this; // Holds onto a reference so cron will work.
global $wpdb;
$wpdb->extend_plugins = $wpdb->prefix . self::PLUGINS_TABLE_NAME;
register_activation_hook(__FILE__, array(&$this,'install'));
register_deactivation_hook(__FILE__, array(&$this,'uninstall'));
if (wp_next_scheduled(self::SCHEDULED_TASK)===false) {
add_filter('cron_schedules', array(&$this, 'cron_schedules'));
wp_schedule_event(time(), 'hourly', self::SCHEDULED_TASK);
}
add_action(self::SCHEDULED_TASK, array(&$this,'sync'));
$this->debug = $debug;
}
function debug($msg) {
if($this->debug) {
if($f = @fopen(self::PLUGIN_KEY . '.log', 'a')) {
fwrite($f, date(DATE_RFC822) . ": $msg\r\n");
fclose($f);
}
}
}
function install() {
global $wpdb;
if($wpdb->get_var("SHOW TABLES LIKE '$wpdb->extend_plugins'") != $wpdb->extend_plugins ||
get_option(self::DB_VERSION_KEY) != self::DB_VERSION)
{
$sql = $this->get_create_table_sql();
require_once(ABSPATH.'/wp-admin/includes/upgrade.php');
dbDelta($sql);
add_option(self::DB_VERSION_KEY, self::DB_VERSION);
}
}
function uninstall() {
//DebugBreak();
global $wpdb;
$sql = $wpdb->prepare("DROP TABLE $wpdb->extend_plugins;");
$wpdb->query($sql);
wp_clear_scheduled_hook(self::SCHEDULED_TASK);
delete_option(self::DB_VERSION_KEY);
delete_option(self::NEXT_PLUGIN_KEY);
}
function cron_schedules() {
return array( 'everyminute' => array('interval' => 10, 'display' => 'Every Minute'));
}
function sync($sync_all=false) {
$plugin_count = -1;
//DebugBreak();
$plugins_to_query = self::BATCH_SIZE;
$first_plugin = intval(get_option(self::NEXT_PLUGIN_KEY));
$offset= intval($first_plugin/self::BATCH_SIZE);
$plugin_count = $this->get_plugin_count();
while($offset*self::BATCH_SIZE+1 <= $plugin_count) {
$next_plugin = ($offset+1)*self::BATCH_SIZE;
if ( $next_plugin > $plugin_count) {
$plugins_to_query = $plugin_count - $offset*self::BATCH_SIZE;
$next_plugin = 0;
}
$first = $offset*self::BATCH_SIZE;
$last = $first + $plugins_to_query - 1;
$this->debug("Syncing info for plugins $first to $last...");
$plugins = $this->get_plugin_list($plugins_to_query,$offset);
if (is_array($plugins))
$plugin_count = count($plugins);
$this->save_plugins($plugins);
$this->write_to_mysql($plugins);
unset($plugins); // Clear the memory;
if (!$sync_all) {
update_option(self::NEXT_PLUGIN_KEY,$next_plugin);
break;
}
$offset++;
}
if ($sync_all) update_option(self::NEXT_PLUGIN_KEY,0);
$this->debug('Done!');
return $plugin_count;
}
function save_plugins($plugins) {
ob_start();
print_r($plugins);
$plugin_text = ob_get_clean();
file_put_contents(self::LAST_BATCH_FILE,$plugin_text);
}
function save_sql($sql) {
file_put_contents(self::LAST_BATCH_SQL,$sql);
}
function write_to_mysql($plugins) {
global $wpdb;
$sql = array();
$sql[] = "REPLACE INTO $wpdb->extend_plugins (slug,name,version,author,author_url,author_link,requires,tested,rating,num_ratings,downloads,last_updated,homepage,description,downloadlink,tags) VALUES ";
$max_tag_len = 0;
$i = 0;
if (is_array($plugins))
foreach($plugins as $plugin) {
$plugin->downloads = intval($plugin->downloads);
$plugin->tags = implode(', ',array_flip($plugin->tags));
$max_tag_len = max(strlen($plugin->tags),$max_tag_len);
$plugin->description = $this->strip_hi_bits($plugin->description);
$author_info = $this->get_author_info($plugin->author);
$plugin->author = $author_info['name'];
$plugin->author_url = $author_info['url'];
$plugin->author_link = $author_info['link'];
$plugin->name = $this->strip_hi_bits($plugin->name);
$plugin->downloadlink = (isset($plugin->downloadlink) ? $plugin->downloadlink : '');
$plugin->downloads = (isset($plugin->downloads) ? intval($plugin->downloads) : -1);
$sql[] = $wpdb->prepare("\r\n\t(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s),",
$plugin->slug, $plugin->name, $plugin->version, $plugin->author, $plugin->author_url, $plugin->author_link,
$plugin->requires, $plugin->tested, $plugin->rating, $plugin->num_ratings, $plugin->downloads,
$plugin->last_updated, $plugin->homepage, $plugin->description,
$plugin->download_link, $plugin->tags); // Note strange bug in API makes downloadlink on request and download_link in object
$i++;
}
unset($plugins); // Clear the memory;
$sql = substr(implode(" ",$sql),0,-1) . ';';
$wpdb->query($sql);
$this->save_sql($sql);
unset($sql); // Clear the memory;
}
function get_author_info($author) {
$author = $this->strip_hi_bits($author);
$author_info = array('name'=>'','url'=>'','link'=>$author);
if (preg_match('#<a href="([^"]+)">([^<]+)</a>#',$author,$matches)) {
$author_info['url'] = $matches[1];
$author_info['name'] = $matches[2];
}
return $author_info;
}
function strip_hi_bits($string) {
$string = utf8_decode($string);
if (preg_match("/[\x80-\xff]/", $string)) {
for ($i=0; $i < strlen($string); $i++) {
if (ord($string{$i})>126) {
$string = substr_replace($string, '?', $i, 1);
}
}
}
return $string;
}
function get_plugin_count() {
$args->search='';
$args->page=1;
$args->per_page=1;
$results = $this->do_action('query_plugins',$args);
return intval($results->info['results']);
}
function get_plugin_list($plugins_to_query,$offset) {
$args->search='';
$args->page= $offset+1;
$args->per_page= $plugins_to_query;
$args->fields = array(
'slug'=>true,
'name'=>true,
'version'=>true,
'author'=>true,
'requires'=>true,
'tested'=>true,
'rating'=>true,
'num_ratings'=>true,
'downloads'=>true,
'last_updated'=>true,
'homepage'=>true,
'description'=>true,
'sections'=>false,
'downloadlink'=>true,
'tags'=> true,
);
$results = $this->do_action('query_plugins',$args);
return $results->plugins;
}
function do_action($action,$args) {
$response = wp_remote_post(self::PLUGIN_API_URL, array(
'body' => array(
'action' => $action,
'request' => serialize($args)
)
)
);
$results = $this->get_results($response);
return $results;
}
function get_results($response) {
if ( is_wp_error($response) ) {
$results = new WP_Error('plugins_api_failed', __('An Unexpected HTTP Error occured during the API request.</p> <p><a href="?" onclick="document.location.reload(); return false;">Try again</a>'), $response->get_error_message() );
} else {
$results = unserialize($response['body']);
if (!$results) {
$results = new WP_Error('plugins_api_failed', __('An unknown error occured'), $request['body']);
}
}
return $results;
}
function get_create_table_sql() {
global $wpdb;
$table_name = $wpdb->prefix . self::PLUGINS_TABLE_NAME;
$sql =<<<CREATE_TABLE
CREATE TABLE {$table_name} (
slug varchar(128) NOT NULL UNIQUE PRIMARY KEY,
name varchar(128) NOT NULL UNIQUE,
version varchar(256),
author varchar(128),
author_url varchar(192),
author_link varchar(256),
requires varchar(256),
tested varchar(256),
rating float unsigned,
num_ratings int(10) unsigned,
downloads int(10) unsigned,
last_updated date,
homepage varchar(255),
description text,
downloadlink varchar(255),
tags varchar(1024)
);
CREATE_TABLE;
return $sql;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment