I’m using Amazon’s CloudFront to speed op my site (a lot; about 80% faster). To make this a no-brainer I made a plugin that converts all local images to CDN URLs. This is not Amazon-specific; just change the $cdn array to match your URLs and it will work.
Disclaimer
Use this at your own risk; this is just a first attempt to write a CDN URL plugin. There will most likely be bugs, but so far it’s working great for me. Code is still a bit messy though; could use some cleanup.
How does it work?
- The plugin is fired just before the page is rendered (OnWebPagePrerender event)
- Every local image (whether inside img, a or input tag) on the page is checked for existence on the CDN
- If a CDN version exists the image URL (src) is replaced by a CDN URL
- The CDN URL is also cached so that it will always be the same (prevents too much checking as well)
- If a CDN version does not exist, the local image is used
<?php
class CDN {
var $cdn; //array of CDN hostnames
var $cdncache = array(); //files already checked to be on CDN
function __construct($output, &$modx) {
$this->modx = $modx;
$this->cdn = array('http://cdna.kenters.com', 'http://cdnb.kenters.com', 'http://cdnc.kenters.com', 'http://cdnd.kenters.com');
//get cached images
$this->cdncache = $this->modx->cacheManager->get('cdncache');
if(!is_array($this->cdncache)) {
$this->cdncache = array();
}
}
function process($output) {
$output = preg_replace_callback('|<img(?:.+?)src\=\"(\S+)\"|', array($this ,'doCDN'), $output);
$output = preg_replace_callback('|<input(?:.+?)src\=\"(\S+)\"|', array($this ,'doCDN'), $output);
$output = preg_replace_callback('|<a(?:.+?)href\=\"(\S+)\"|', array($this ,'doCDN'), $output);
$this->modx->cacheManager->set('cdncache', $this->cdncache,7200);
return $output;
}
function doCDN($match) {
//only process local images
if( (!stripos($match[1], '.jpg') && !stripos($match[1], '.png') && !stripos($match[1], '.gif') || stripos($match[1], 'http://') !== false || stripos($match[1], 'https://') !==false || stripos($match[1], 'mailto:') !== false) )
{
return $match[0];
}
else
{
$replace = $match[1]; //text to replace
if(array_key_exists($replace, $this->cdncache)) {
$replaced = $this->cdncache[$replace];
return str_replace($replace, $replaced, $match[0]);
}
else {
$replaced = $this->cdn[array_rand($this->cdn)].( substr($match[1], 0, 1) == '/' ? '' : '/' ).$match[1]; //replaced url
if( $this->url_validate($replaced) ) {
$this->cdncache[$replace] = $replaced; //add to cache
return str_replace($replace, $replaced, $match[0]);
}
else {
return $match[0];
}
}
}
}
function url_validate( $link )
{
$url_parts = @parse_url( $link );
if ( empty( $url_parts["host"] ) ) return( false );
if ( !empty( $url_parts["path"] ) )
{
$documentpath = $url_parts["path"];
}
else
{
$documentpath = "/";
}
if ( !empty( $url_parts["query"] ) )
{
$documentpath .= "?" . $url_parts["query"];
}
$host = $url_parts["host"];
$port = $url_parts["port"];
if (empty( $port ) ) $port = "80";
$socket = fsockopen( $host, $port, $errno, $errstr, 30 );
if (!$socket)
{
return(false);
}
else
{
fwrite ($socket, "HEAD ".$documentpath." HTTP/1.0\r\nHost: $host\r\n\r\n");
$http_response = fgets( $socket, 22 );
if ( stripos($http_response, "200 OK") )
{
return(true);
fclose( $socket );
} else
{
return(false);
}
}
}
}
$output = &$modx->resource->_output;
$cdn = new CDN($output, $modx);
$output = $cdn->process($output);