Wordpress Image Optimizer

By mrbagnall | 27 March 2016

WordPress isn't native tongue to me. That said, I feel as competitent with it as I do any standard PHP content management system not written in an unfamiliar framework. Recently, I had been tasked with optimizing a WordPress site that was front heavy, slow loading, and experiencing a ton of show view times and high bounce rates.

The first thing I noticed were the uncompressed screen captures placed in the posts via a WYSIWYG. These had been "constrained" using image dimensions and not cropped, prepared and compressed prior to use. In addition, no caching mechanism had been implemented to make subsequent loading of pages quick for anonymous users.

Seeing the file size (in megs) issue as the greatest challenge, that is what I sought to solve. I wrote a script that goes through the image folder and compresses and constrains the images to a dimension, and then replaces them in the WordPress posts so that they refer to the new image instead of the older ones. Note that this mechanism I have come up with converts all images to JPEG images, so if you have transparent PNG files on your site, there will need to be some further modification to this script. It will also have the unintended side-effect of altering images on links to external sites. This will be addressed in a future revision. If you wish to make a pull request against this, you can do so at the GitHub link located below the code 

The comments here are self-explanatory - so have it it. Feel free to drop me any questions or issues on GitHub or using the contact link at the bottom of this page.

The first part of our script deals with initializing the database connection. Be sure to provide the connection details to your WordPress database in the defines at the top of the script. Then it processes the images and updates the database.

/**
 * Image Optimizer
 */

// Open up our database connection for when we modify the content post-optimization.
define('DB_HOST', '');
define('DB_NAME', '');
define('DB_USERNAME', '');
define('DB_PASSWORD', '');
try {
  // Open our database connection and prepare to read in our date
  $dbh = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USERNAME, DB_PASSWORD);
} 
catch (PDOException $e) {
  echo "There was a failure in our database and the script was not able to run:\n";
  echo $e->getMessage();
  exit();
}

// Our confiugurable definitions
define('BASE_IMAGE_PATH', 'wp-content/uploads');
define('MAX_IMAGE_WIDTH', 900);

// Run our recursive function. Works through all folders and files.
recursive_image_check(BASE_IMAGE_PATH);

// Update the references in our database. Make them point to jpeg as opposed to png.
// This has one serious drawback. It does not check for external links, so it could,
// and very likely will, break external links to png images. For our purposes, we 
// need to do it in the interest of time.
$query = "SELECT * FROM wp_posts ORDER BY ID DESC";
$result = $dbh->query($query);
$test = 0;
foreach($result as $row) {
  $content = $row['post_content'];
  $content = str_replace('.png', '.jpg', $content);
  $content = str_replace('http://www.example.com', '', $content);
  
  $write = "UPDATE wp_posts SET post_content = '" . addslashes($content) . "' 
            WHERE ID=" . $row['ID'] . ";";
  $dbh->query($write);
  
  if ($row['post_mime_type'] == 'image/png') {
    $path = substr($row['guid'], -3) . '.jpg';
    $write = "UPDATE wp_posts SET guid = '" . addslashes($path) . "', 
                     post_mime_type = 'image/jpeg' WHERE ID=" . $row['ID'] . ";";
    $dbh->query($write);
  }
}

echo 'completed.';
exit();

The final part is the recursive function to prepare our images properly. You can change the maximin width by modifying the MAX_IMAGE_WIDTH constant above. Also if you need to change the location of where your wp-content/uploads directory is, you can specify that as well.

/**
 * Go through our folders recursively looking for, and converting, image files to the
 * jpeg format.
 */
function recursive_image_check($directory) {
  if (!file_exists($directory) || !is_dir($directory)) {
    return FALSE;
  }
  if (!is_readable($directory)) {
    return FALSE;
  }
  $dh = opendir($directory);
    while ( ( $item = readdir($dh) ) !== FALSE ) {
    if ($item != '.' && $item != '..') {
      
      // The full path is made up of the directory and the item associated with our
      // current check.
      $path = $directory . '/' . $item;
      
      // If our path is a directory, then dive into it.
      if (is_dir($path)) {
        recursive_image_check($path);
      }
      else {
        // Get the information on the file we are currently working with.
        $info = pathinfo($path);
      
        // If we are working with a PNG, we need to copy it at our re-sampled size and
        // convert it to a JPG. We put this in a new directory to keep it from being
        // destructive to our existing files giving us the opportunity to test results.
        if ($info['extension'] == 'png') {
          $image = @imagecreatefrompng($path);
          if ($image === FALSE) {
            $bad_images[] = $path;
            continue;
          }
        }
        if ($info['extension'] == 'jpg') {
          $image = @imagecreatefromjpeg($path);
          if ($image === FALSE) {
            $bad_images[] = $path;
            continue;
          }
        }
        $image_size = getimagesize($path);
        $width = $image_size[0];
        $height = $image_size[1];
        
        // If we are greater than our given width, then scale the image down to our
        // max width (x). 
        if ($width >  MAX_IMAGE_WIDTH) {
          $percent = MAX_IMAGE_WIDTH / $width;
          $new_width = MAX_IMAGE_WIDTH;
          $new_height = $height * $percent;
          $new_image = imagecreatetruecolor($new_width, $new_height);
        }
        else {
          $new_width = $width;
          $new_height = $height;
          $new_image = imagecreatetruecolor($width, $height);
        }

        // Create the home for our new files.
        $new_directory = 'new/' . $info['dirname'];
        if (!file_exists($new_directory)) {
          mkdir($new_directory, 0777, TRUE);
        }
                
        // Set up the new path including our new home and file name with new extension.
        $new_path = $new_directory . '/' . $info['filename'] . '.jpg';

        // Make a copy of the image at our new dimensions (if appropriate) and save as
        // a JPG.
        imagecopyresized($new_image, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
        imagejpeg($new_image, $new_path, 75);
        unset($new_image, $new_path, $image);
      }
    }
  }
  closedir($dh);
}

GitHub Repository: https://github.com/ElusiveMind/wp_image_optimizer