All the Sites, All the Plugins

WordPress gets complex as a multisite get bigger. Things that worked at smaller scale start dying.

I’ve long wanted to be able to run a report on all the active plugins on our multisite but it always timed out before it would complete.

This script will spit out 1001 sites at a time into a table and list any active plugins. It’ll also add pagination to go forward below the table. That same pagination can be manipulated directly in the URL by changing /?id=1 to whatever number you’d want.

I was attempting to do this with MYSQL and OFFSET but ran into timeout issues when I hit LIMIT 4000 and OFFSET 3200 (displays 800 records but starting at the 3200th record). It was an awkward and inefficient and ultimately broke. I learned some things though.

This was the post that got me moving in the right direction. I’d searched for ways to batch call MYSQL statements but wasn’t quite getting what I wanted. Looking at it now, it makes sense and I feel like I should have seen that path sooner but it’s now a pattern I understand and will be using in some other ways as well.

I should be able to roll all the results into one big variable and spit it out, or write it incrementally to a static file. That opens all sorts of doors.

if (empty($_GET['id'])){
$last_id = 1;
else {
    $last_id = urlencode($_GET['id']);
$query = "SELECT * FROM {$wpdb->blogs} WHERE site_id = {$wpdb->siteid} AND blog_id >".$last_id." LIMIT 100"; 
    $blogs = $wpdb->get_results($query);
            echo '<table class="table table-striped" >';
    foreach ( $blogs as $blog ) {
        // Do your thing!
        $the_plugs = get_blog_option($blog->blog_id, 'active_plugins');         
            echo '<tr><td><a href="'. get_blog_option($blog->blog_id, 'siteurl') . '">' . get_blog_option($blog->blog_id, 'blogname') .'</a> - ' . $blog->blog_id .'</td><td>' . get_blog_option($blog->blog_id, 'siteurl') . '</td>';
            echo '<td>plugins</td><td><ol>';
            foreach($the_plugs as $key => $value) {
                $string = explode('/',$value); // Folder name will be displayed
                echo '<li>'.$string[0] .'</li>';
            echo '</ol></td></tr>';
    echo '</table>';
          // Record the last ID for the next loop
        $last_id = $blog->blog_id;
    echo '<a class="btn btn-primary" href="?id=' . $last_id . '">next round of records ' . $last_id . '</a>';

1 I could probably push it to 700 or so but this keeps things snappy.

Comments on this post

  1. Stephane Daury said on April 1, 2016 at 2:29 pm

    Glad the post was helpful. 🙂

    Note: as a general rule, wpdb::prepare() is pretty awesome.

    • Tom Woodward said on April 1, 2016 at 3:02 pm

      Appreciate the tip and thanks for documenting how you’re doing things.


  2. Tim Owens said on April 1, 2016 at 7:29 pm

    Writing to a file and concatenating with each run could be useful since you could skip all sites with no plugins active and essentially have a clean report of plugins across all sites. Bonus points, write it as CSV with the plugins broken down by column or something so you could start sorting by usage (it’s too late Friday for me to think what the best way to massage the data would be to get there but to me the real gem would be saying “Oh wow, this plugin only has 3 sites using it” versus “Holy crap we were going to sunset that plugin but it’s used on 300+ sites currently.”

  3. Tom said on April 1, 2016 at 7:55 pm

    I just landed in Boston but will knock that out this week.