Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.0.0 #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 117 additions & 31 deletions WordPress-Dropins/wp-stack-cdn.php
Original file line number Diff line number Diff line change
@@ -1,73 +1,159 @@
<?php
/*
Plugin Name: WP Stack CDN
Version: 0.3
Author: Mark Jaquith
Author URI: http://coveredwebservices.com/
Version: 1.0.2
Author: Mark Jaquith, Matthew Sigley
*/

// Convenience methods
if(!class_exists('WP_Stack_Plugin')){class WP_Stack_Plugin{function hook($h){$p=10;$m=$this->sanitize_method($h);$b=func_get_args();unset($b[0]);foreach((array)$b as $a){if(is_int($a))$p=$a;else $m=$a;}return add_action($h,array($this,$m),$p,999);}private function sanitize_method($m){return str_replace(array('.','-'),array('_DOT_','_DASH_'),$m);}}}

// The plugin
class WP_Stack_CDN_Plugin extends WP_Stack_Plugin {
private static $object = null;

public static $instance;
public $site_domain;
public $cdn_domain;
public $upload_dir;
public $uploads_only;
public $force_https;
public $production;
public $staging;
public $filter_urls;
public $extensions;
public $resource_key;

public function __construct() {
private function __construct() {
self::$instance = $this;
$this->hook( 'plugins_loaded' );
}

static function &object() {
if ( ! self::$object instanceof WP_Stack_CDN_Plugin ) {
self::$object = new WP_Stack_CDN_Plugin();
}
return self::$object;
}

public function plugins_loaded() {
$domain_set_up = get_option( 'wp_stack_cdn_domain' ) || ( defined( 'WP_STACK_CDN_DOMAIN' ) && WP_STACK_CDN_DOMAIN );
$production = defined( 'WP_STAGE' ) && WP_STAGE === 'production';
$staging = defined( 'WP_STAGE' ) && WP_STAGE === 'staging';
$uploads_only = defined( 'WP_STACK_CDN_UPLOADS_ONLY' ) && WP_STACK_CDN_UPLOADS_ONLY;
if ( $domain_set_up && !$staging && ( $production || $uploads_only ) )
$this->staging = defined( 'WP_STAGE' ) && WP_STAGE === 'staging';
if ( $domain_set_up && !$this->staging )
$this->hook( 'init' );
}

public function init() {
$this->uploads_only = apply_filters( 'wp_stack_cdn_uploads_only', defined( 'WP_STACK_CDN_UPLOADS_ONLY' ) ? WP_STACK_CDN_UPLOADS_ONLY : false );
if( is_admin() )
return; //Don't filter admin pages

$this->production = defined( 'WP_STAGE' ) && WP_STAGE === 'production';

if ( $this->production ) {
$this->hook( 'wp_stack_cdn_content', 'filter' );
} else {
$this->filter_urls = array();
$uploads = apply_filters( 'wp_stack_cdn_uploads', defined( 'WP_STACK_CDN_UPLOADS' ) ? WP_STACK_CDN_UPLOADS : false );
if ( $uploads ) {
$upload_dir = wp_upload_dir();
$this->filter_urls[] = $upload_dir['baseurl'];
}
$theme = apply_filters( 'wp_stack_cdn_theme', defined( 'WP_STACK_CDN_THEME' ) ? WP_STACK_CDN_THEME : false );
if ( $theme )
$this->filter_urls[] = get_template_directory_uri();

$this->filter_urls = apply_filters( 'wp_stack_cdn_filter_urls', $this->filter_urls );
if( empty( $this->filter_urls ) )
return; //Nothing to filter

$this->hook( 'wp_stack_cdn_content', 'filter_urls' );
}

$this->extensions = apply_filters( 'wp_stack_cdn_extensions', array( 'jpe?g', 'gif', 'png', 'css', 'bmp', 'js', 'ico', 'svg', 'webp' ) );
if ( !is_admin() ) {
$this->hook( 'template_redirect' );
if ( $this->uploads_only )
$this->hook( 'wp_stack_cdn_content', 'filter_uploads_only' );
else
$this->hook( 'wp_stack_cdn_content', 'filter' );
$this->site_domain = parse_url( get_bloginfo( 'url' ), PHP_URL_HOST );
$this->cdn_domain = defined( 'WP_STACK_CDN_DOMAIN' ) ? WP_STACK_CDN_DOMAIN : get_option( 'wp_stack_cdn_domain' );
$this->site_domain = parse_url( get_bloginfo( 'url' ), PHP_URL_HOST );
$this->cdn_domain = defined( 'WP_STACK_CDN_DOMAIN' ) ? WP_STACK_CDN_DOMAIN : get_option( 'wp_stack_cdn_domain' );
$this->force_https = defined( 'WP_STACK_CDN_FORCE_HTTPS' ) ? WP_STACK_CDN_FORCE_HTTPS : true;
$this->resource_key = defined( 'WP_STACK_CDN_RESOURCE_KEY' ) ? WP_STACK_CDN_RESOURCE_KEY : date('Ymd', current_time('timestamp'));

$this->hook( 'template_redirect' );
$this->hook( 'wp_head', 'prefetch' );
}

public function filter_urls( $content ) {
$cache_key = hash( 'sha256', $content ) . '_' . $_SERVER['REQUEST_URI'] . '_' . serialize( $this->filter_urls );
$cached_content = wp_cache_get( $cache_key, 'wp_stack_cdn_filter_urls' );
if( false !== $cached_content )
return $cached_content;

foreach( $this->filter_urls as $url ) {
$domain = preg_quote( parse_url( $url, PHP_URL_HOST ), '#' );
$path = untrailingslashit( parse_url( $url, PHP_URL_PATH ) );
$preg_path = preg_quote( $path, '#' );

// Targeted replace just on URL
// Replace URLs with query strings
$replacement = '=$1//' . $this->cdn_domain . $path . '/$3.$4$5&amp;resource_key='.$this->resource_key.'$1';
if( $this->force_https ) //Force https for HTTP/2 and SPDY support
$replacement = '=$1https://' . $this->cdn_domain . $path . '/$3.$4$5&amp;resource_key='.$this->resource_key.'$1';
$content = preg_replace( "#=([\"'])(https?://{$domain})?$preg_path/([^(?:\\1)\]\?]+)\.(" . implode( '|', $this->extensions ) . ")(\?((?:(?!\\1).)+))\\1#", $replacement, $content );

//Replace URLs without query strings
$replacement = '=$1//' . $this->cdn_domain . $path . '/$3.$4?&amp;resource_key='.$this->resource_key.'$1';
if( $this->force_https ) //Force https for HTTP/2 and SPDY support
$replacement = '=$1https://' . $this->cdn_domain . $path . '/$3.$4?&amp;resource_key='.$this->resource_key.'$1';
$content = preg_replace( "#=([\"'])(https?://{$domain})?$preg_path/([^(?:\\1)\]\?]+)\.(" . implode( '|', $this->extensions ) . ")(?:\?)?\\1#", $replacement, $content );
}

wp_cache_set( $cache_key, $content, 'wp_stack_cdn_filter_urls', WEEK_IN_SECONDS );
return $content;
}

public function filter_uploads_only( $content ) {
$upload_dir = wp_upload_dir();
$upload_dir = $upload_dir['baseurl'];
$domain = preg_quote( parse_url( $upload_dir, PHP_URL_HOST ), '#' );
$path = parse_url( $upload_dir, PHP_URL_PATH );
$preg_path = preg_quote( $path, '#' );
public function filter( $content ) {
$cache_key = hash( 'sha256', $content ) . '_' . $_SERVER['REQUEST_URI'];
$cached_content = wp_cache_get( $cache_key, 'wp_stack_cdn_filter_urls' );
if( false !== $cached_content )
return $cached_content;

// Replace URLs with query strings
$replacement = '=$1//' . $this->cdn_domain . '/$3.$4$5&amp;resource_key='.$this->resource_key.'$1';
if( $this->force_https ) //Force https for HTTP/2 and SPDY support
$replacement = '=$1https://' . $this->cdn_domain . '/$3.$4$5&amp;resource_key='.$this->resource_key.'$1';
$content = preg_replace( "#=([\"'])(https?://{$this->site_domain})?/([^/][^(?:\\1)\?]+)\.(" . implode( '|', $this->extensions ) . ")(\?((?:(?!\\1).)+))\\1#", $replacement, $content );

// Targeted replace just on uploads URLs
return preg_replace( "#([\"'])(https?://{$domain})?$preg_path/((?:(?!\\1]).)+)\.(" . implode( '|', $this->extensions ) . ")(\?((?:(?!\\1).)+))?\\1#", '$1//' . $this->cdn_domain . $path . '/$3.$4$5$1', $content );
//Replace URLs without query strings
$replacement = '=$1//' . $this->cdn_domain . '/$3.$4?&amp;resource_key='.$this->resource_key.'$1';
if( $this->force_https ) //Force https for HTTP/2 and SPDY support
$replacement = '=$1https://' . $this->cdn_domain . '/$3.$4?&amp;resource_key='.$this->resource_key.'$1';
$content = preg_replace( "#=([\"'])(https?://{$this->site_domain})?/([^/][^(?:\\1)\?]+)\.(" . implode( '|', $this->extensions ) . ")(?:\?)?\\1#", $replacement, $content );

wp_cache_set( $cache_key, $content, 'wp_stack_cdn_filter', WEEK_IN_SECONDS );
return $content;
}

public function filter( $content ) {
return preg_replace( "#([\"'])(https?://{$this->site_domain})?/([^/](?:(?!\\1).)+)\.(" . implode( '|', $this->extensions ) . ")(\?((?:(?!\\1).)+))?\\1#", '$1//' . $this->cdn_domain . '/$3.$4$5$1', $content );
public function prefetch(){
echo '<link rel="preconnect" href="' . ( $this->force_https ? 'https' : '' ) . '//'.$this->cdn_domain.'" crossorigin>' . "\n";
}

public function template_redirect() {
ob_start( array( $this, 'ob' ) );
}

public function ob( $contents ) {
return apply_filters( 'wp_stack_cdn_content', $contents, $this );
return apply_filters( 'wp_stack_cdn_content', $contents, $this );
}
}

new WP_Stack_CDN_Plugin;
$WP_Stack_CDN_Plugin = WP_Stack_CDN_Plugin::object();

//API Functions
function WP_Stack_CDN_get_url( $url ) {
$WP_Stack_CDN_Plugin = WP_Stack_CDN_Plugin::object();
if ( !empty( $WP_Stack_CDN_Plugin->cdn_domain ) && !$WP_Stack_CDN_Plugin->staging ) {
list($protocol, $uri) = explode('://', $url, 2);
if( $WP_Stack_CDN_Plugin->force_https )
$protocol = 'https'; //Force https for HTTP/2 and SPDY support
$url = $protocol . '://' . $WP_Stack_CDN_Plugin->cdn_domain;
$path_pos = stripos( $uri, '/' );
if( false !== $path_pos )
$url .= substr( $uri, $path_pos );
}
return $url;
}