| @@ -1,135 +1,78 @@ | ||
| 1 | 1 | <?php |
| 2 | -/** | |
| 3 | - * Base class other (more-specific) classes inherit from. | |
| 4 | - */ | |
| 2 | +if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly | |
| 5 | 3 | |
| 6 | -if ( ! defined( 'ABSPATH' ) ) { | |
| 7 | - exit; | |
| 8 | -} | |
| 9 | - | |
| 10 | -abstract class autoptimizeBase | |
| 11 | -{ | |
| 12 | - /** | |
| 13 | - * Holds content being processed (html, scripts, styles) | |
| 14 | - * | |
| 15 | - * @var string | |
| 16 | - */ | |
| 4 | +abstract class autoptimizeBase { | |
| 17 | 5 | protected $content = ''; |
| 18 | - | |
| 19 | - /** | |
| 20 | - * Controls debug logging. | |
| 21 | - * | |
| 22 | - * @var bool | |
| 23 | - */ | |
| 24 | - public $debug_log = false; | |
| 25 | - | |
| 26 | - /** | |
| 27 | - * Initiated $cdn_url. | |
| 28 | - * | |
| 29 | - * @var string | |
| 30 | - */ | |
| 31 | - public $cdn_url = ''; | |
| 32 | - | |
| 33 | - public function __construct( $content ) | |
| 34 | - { | |
| 6 | + protected $tagWarning = false; | |
| 7 | + | |
| 8 | + public function __construct($content) { | |
| 35 | 9 | $this->content = $content; |
| 36 | 10 | } |
| 37 | - | |
| 38 | - /** | |
| 39 | - * Reads the page and collects tags. | |
| 40 | - * | |
| 41 | - * @param array $options Options. | |
| 42 | - * | |
| 43 | - * @return bool | |
| 44 | - */ | |
| 45 | - abstract public function read( $options ); | |
| 46 | - | |
| 47 | - /** | |
| 48 | - * Joins and optimizes collected things. | |
| 49 | - * | |
| 50 | - * @return bool | |
| 51 | - */ | |
| 11 | + | |
| 12 | + //Reads the page and collects tags | |
| 13 | + abstract public function read($justhead); | |
| 14 | + | |
| 15 | + //Joins and optimizes collected things | |
| 52 | 16 | abstract public function minify(); |
| 53 | - | |
| 54 | - /** | |
| 55 | - * Caches the things. | |
| 56 | - * | |
| 57 | - * @return void | |
| 58 | - */ | |
| 17 | + | |
| 18 | + //Caches the things | |
| 59 | 19 | abstract public function cache(); |
| 60 | - | |
| 61 | - /** | |
| 62 | - * Returns the content | |
| 63 | - * | |
| 64 | - * @return string | |
| 65 | - */ | |
| 20 | + | |
| 21 | + //Returns the content | |
| 66 | 22 | abstract public function getcontent(); |
| 67 | - | |
| 68 | - /** | |
| 69 | - * Tranfsorms a given URL to a full local filepath if possible. | |
| 70 | - * Returns local filepath or false. | |
| 71 | - * | |
| 72 | - * @param string $url URL to transform. | |
| 73 | - * | |
| 74 | - * @return bool|string | |
| 75 | - */ | |
| 76 | - public function getpath( $url ) | |
| 77 | - { | |
| 78 | - $url = apply_filters( 'autoptimize_filter_cssjs_alter_url', $url ); | |
| 79 | - | |
| 80 | - if ( false !== strpos( $url, '%' ) ) { | |
| 81 | - $url = urldecode( $url ); | |
| 23 | + | |
| 24 | + //Converts an URL to a full path | |
| 25 | + protected function getpath($url) { | |
| 26 | + $url=apply_filters( 'autoptimize_filter_cssjs_alter_url', $url); | |
| 27 | + | |
| 28 | + if (strpos($url,'%')!==false) { | |
| 29 | + $url=urldecode($url); | |
| 82 | 30 | } |
| 83 | 31 | |
| 84 | - $site_host = parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ); | |
| 85 | - $content_host = parse_url( AUTOPTIMIZE_WP_ROOT_URL, PHP_URL_HOST ); | |
| 86 | - | |
| 87 | - // Normalizing attempts... | |
| 88 | - $double_slash_position = strpos( $url, '//' ); | |
| 89 | - if ( 0 === $double_slash_position ) { | |
| 90 | - if ( is_ssl() ) { | |
| 91 | - $url = 'https:' . $url; | |
| 32 | + $siteHost=parse_url(AUTOPTIMIZE_WP_SITE_URL,PHP_URL_HOST); | |
| 33 | + | |
| 34 | + // normalize | |
| 35 | + if (strpos($url,'//')===0) { | |
| 36 | + if (is_ssl()) { | |
| 37 | + $url = "https:".$url; | |
| 92 | 38 | } else { |
| 93 | - $url = 'http:' . $url; | |
| 39 | + $url = "http:".$url; | |
| 94 | 40 | } |
| 95 | - } elseif ( ( false === $double_slash_position ) && ( false === strpos( $url, $site_host ) ) ) { | |
| 96 | - if ( AUTOPTIMIZE_WP_SITE_URL === $site_host ) { | |
| 97 | - $url = AUTOPTIMIZE_WP_SITE_URL . $url; | |
| 41 | + } else if ((strpos($url,'//')===false) && (strpos($url,$siteHost)===false)) { | |
| 42 | + if (AUTOPTIMIZE_WP_SITE_URL === $siteHost) { | |
| 43 | + $url = AUTOPTIMIZE_WP_SITE_URL.$url; | |
| 98 | 44 | } else { |
| 99 | - $url = AUTOPTIMIZE_WP_SITE_URL . autoptimizeUtils::path_canonicalize( $url ); | |
| 45 | + $subdir_levels=substr_count(preg_replace("/https?:\/\//","",AUTOPTIMIZE_WP_SITE_URL),"/"); | |
| 46 | + $url = AUTOPTIMIZE_WP_SITE_URL.str_repeat("/..",$subdir_levels).$url; | |
| 100 | 47 | } |
| 101 | 48 | } |
| 102 | 49 | |
| 103 | - if ( $site_host !== $content_host ) { | |
| 104 | - $url = str_replace( AUTOPTIMIZE_WP_CONTENT_URL, AUTOPTIMIZE_WP_SITE_URL . AUTOPTIMIZE_WP_CONTENT_NAME, $url ); | |
| 105 | - } | |
| 106 | - | |
| 107 | - // First check; hostname wp site should be hostname of url! | |
| 108 | - $url_host = @parse_url( $url, PHP_URL_HOST ); // @codingStandardsIgnoreLine | |
| 109 | - if ( $url_host !== $site_host ) { | |
| 110 | - /** | |
| 111 | - * First try to get all domains from WPML (if available) | |
| 112 | - * then explicitely declare $this->cdn_url as OK as well | |
| 113 | - * then apply own filter autoptimize_filter_cssjs_multidomain takes an array of hostnames | |
| 114 | - * each item in that array will be considered part of the same WP multisite installation | |
| 115 | - */ | |
| 50 | + // first check; hostname wp site should be hostname of url | |
| 51 | + $thisHost=@parse_url($url,PHP_URL_HOST); | |
| 52 | + if ($thisHost !== $siteHost) { | |
| 53 | + /* | |
| 54 | + * first try to get all domains from WPML (if available) | |
| 55 | + * then explicitely declare $this->cdn_url as OK as well | |
| 56 | + * then apply own filter autoptimize_filter_cssjs_multidomain takes an array of hostnames | |
| 57 | + * each item in that array will be considered part of the same WP multisite installation | |
| 58 | + */ | |
| 116 | 59 | $multidomains = array(); |
| 117 | - | |
| 118 | - $multidomains_wpml = apply_filters( 'wpml_setting', array(), 'language_domains' ); | |
| 119 | - if ( ! empty( $multidomains_wpml ) ) { | |
| 120 | - $multidomains = array_map( array( $this, 'get_url_hostname' ), $multidomains_wpml ); | |
| 60 | + | |
| 61 | + $multidomainsWPML = apply_filters('wpml_setting', array(), 'language_domains'); | |
| 62 | + if (!empty($multidomainsWPML)) { | |
| 63 | + $multidomains = array_map(array($this,"ao_getDomain"),$multidomainsWPML); | |
| 121 | 64 | } |
| 122 | - | |
| 123 | - if ( ! empty( $this->cdn_url ) ) { | |
| 124 | - $multidomains[] = parse_url( $this->cdn_url, PHP_URL_HOST ); | |
| 65 | + | |
| 66 | + if (!empty($this->cdn_url)) { | |
| 67 | + $multidomains[]=parse_url($this->cdn_url,PHP_URL_HOST); | |
| 125 | 68 | } |
| 126 | - | |
| 127 | - $multidomains = apply_filters( 'autoptimize_filter_cssjs_multidomain', $multidomains ); | |
| 128 | - | |
| 129 | - if ( ! empty( $multidomains ) ) { | |
| 130 | - if ( in_array( $url_host, $multidomains ) ) { | |
| 131 | - $url = str_replace( $url_host, $site_host, $url ); | |
| 69 | + | |
| 70 | + $multidomains = apply_filters('autoptimize_filter_cssjs_multidomain', $multidomains); | |
| 71 | + | |
| 72 | + if (!empty($multidomains)) { | |
| 73 | + if (in_array($thisHost,$multidomains)) { | |
| 74 | + $url=str_replace($thisHost, parse_url(AUTOPTIMIZE_WP_SITE_URL,PHP_URL_HOST), $url); | |
| 132 | 75 | } else { |
| 133 | 76 | return false; |
| 134 | 77 | } |
| 135 | 78 | } else { |
| @@ -135,556 +78,251 @@ | ||
| 135 | 78 | } else { |
| 136 | 79 | return false; |
| 137 | 80 | } |
| 138 | 81 | } |
| 139 | - | |
| 140 | - // Try to remove "wp root url" from url while not minding http<>https. | |
| 141 | - $tmp_ao_root = preg_replace( '/https?:/', '', AUTOPTIMIZE_WP_ROOT_URL ); | |
| 142 | - | |
| 143 | - if ( $site_host !== $content_host ) { | |
| 144 | - // As we replaced the content-domain with the site-domain, we should match against that. | |
| 145 | - $tmp_ao_root = preg_replace( '/https?:/', '', AUTOPTIMIZE_WP_SITE_URL ); | |
| 82 | + | |
| 83 | + // try to remove "wp root url" from url while not minding http<>https | |
| 84 | + $tmp_ao_root = preg_replace('/https?:/','',AUTOPTIMIZE_WP_ROOT_URL); | |
| 85 | + $tmp_url = preg_replace('/https?:/','',$url); | |
| 86 | + $path = str_replace($tmp_ao_root,'',$tmp_url); | |
| 87 | + | |
| 88 | + // final check; if path starts with :// or //, this is not a URL in the WP context and we have to assume we can't aggregate | |
| 89 | + if (preg_match('#^:?//#',$path)) { | |
| 90 | + /** External script/css (adsense, etc) */ | |
| 91 | + return false; | |
| 146 | 92 | } |
| 147 | 93 | |
| 148 | - $tmp_url = preg_replace( '/https?:/', '', $url ); | |
| 149 | - $path = str_replace( $tmp_ao_root, '', $tmp_url ); | |
| 94 | + $path = str_replace('//','/',WP_ROOT_DIR.$path); | |
| 95 | + return $path; | |
| 96 | + } | |
| 150 | 97 | |
| 151 | - // If path starts with :// or //, this is not a URL in the WP context and | |
| 152 | - // we have to assume we can't aggregate. | |
| 153 | - if ( preg_match( '#^:?//#', $path ) ) { | |
| 154 | - // External script/css (adsense, etc). | |
| 155 | - return false; | |
| 98 | + // needed for WPML-filter | |
| 99 | + protected function ao_getDomain($in) { | |
| 100 | + // make sure the url starts with something vaguely resembling a protocol | |
| 101 | + if ((strpos($in,"http")!==0) && (strpos($in,"//")!==0)) { | |
| 102 | + $in="http://".$in; | |
| 156 | 103 | } |
| 104 | + | |
| 105 | + // do the actual parse_url | |
| 106 | + $out = parse_url($in,PHP_URL_HOST); | |
| 107 | + | |
| 108 | + // fallback if parse_url does not understand the url is in fact a url | |
| 109 | + if (empty($out)) $out=$in; | |
| 110 | + | |
| 111 | + return $out; | |
| 112 | + } | |
| 157 | 113 | |
| 158 | - // Prepend with WP_ROOT_DIR to have full path to file. | |
| 159 | - $path = str_replace( '//', '/', WP_ROOT_DIR . $path ); | |
| 160 | 114 | |
| 161 | - // Final check: does file exist and is it readable? | |
| 162 | - if ( file_exists( $path ) && is_file( $path ) && is_readable( $path ) ) { | |
| 163 | - return $path; | |
| 115 | + // logger | |
| 116 | + protected function ao_logger($logmsg,$appendHTML=true) { | |
| 117 | + if ($appendHTML) { | |
| 118 | + $logmsg="<!--noptimize--><!-- ".$logmsg." --><!--/noptimize-->"; | |
| 119 | + $this->content.=$logmsg; | |
| 164 | 120 | } else { |
| 165 | - return false; | |
| 121 | + error_log("Autoptimize: ".$logmsg); | |
| 166 | 122 | } |
| 167 | 123 | } |
| 168 | 124 | |
| 169 | - /** | |
| 170 | - * Returns the hostname part of a given $url if we're able to parse it. | |
| 171 | - * If not, it returns the original url (prefixed with http:// scheme in case | |
| 172 | - * it was missing). | |
| 173 | - * Used as callback for WPML multidomains filter. | |
| 174 | - * | |
| 175 | - * @param string $url URL. | |
| 176 | - * | |
| 177 | - * @return string | |
| 178 | - */ | |
| 179 | - protected function get_url_hostname( $url ) | |
| 180 | - { | |
| 181 | - // Checking that the url starts with something vaguely resembling a protocol. | |
| 182 | - if ( ( 0 !== strpos( $url, 'http' ) ) && ( 0 !== strpos( $url, '//' ) ) ) { | |
| 183 | - $url = 'http://' . $url; | |
| 125 | + // hide everything between noptimize-comment tags | |
| 126 | + protected function hide_noptimize($noptimize_in) { | |
| 127 | + if ( preg_match( '/<!--\s?noptimize\s?-->/', $noptimize_in ) ) { | |
| 128 | + $noptimize_out = preg_replace_callback( | |
| 129 | + '#<!--\s?noptimize\s?-->.*?<!--\s?/\s?noptimize\s?-->#is', | |
| 130 | + create_function( | |
| 131 | + '$matches', | |
| 132 | + 'return "%%NOPTIMIZE".AUTOPTIMIZE_HASH."%%".base64_encode($matches[0])."%%NOPTIMIZE%%";' | |
| 133 | + ), | |
| 134 | + $noptimize_in | |
| 135 | + ); | |
| 136 | + } else { | |
| 137 | + $noptimize_out = $noptimize_in; | |
| 184 | 138 | } |
| 185 | - | |
| 186 | - // Grab the hostname. | |
| 187 | - $hostname = parse_url( $url, PHP_URL_HOST ); | |
| 188 | - | |
| 189 | - // Fallback when parse_url() fails. | |
| 190 | - if ( empty( $hostname ) ) { | |
| 191 | - $hostname = $url; | |
| 139 | + return $noptimize_out; | |
| 140 | + } | |
| 141 | + | |
| 142 | + // unhide noptimize-tags | |
| 143 | + protected function restore_noptimize($noptimize_in) { | |
| 144 | + if ( strpos( $noptimize_in, '%%NOPTIMIZE%%' ) !== false ) { | |
| 145 | + $noptimize_out = preg_replace_callback( | |
| 146 | + '#%%NOPTIMIZE'.AUTOPTIMIZE_HASH.'%%(.*?)%%NOPTIMIZE%%#is', | |
| 147 | + create_function( | |
| 148 | + '$matches', | |
| 149 | + 'return base64_decode($matches[1]);' | |
| 150 | + ), | |
| 151 | + $noptimize_in | |
| 152 | + ); | |
| 153 | + } else { | |
| 154 | + $noptimize_out = $noptimize_in; | |
| 192 | 155 | } |
| 193 | - | |
| 194 | - return $hostname; | |
| 156 | + return $noptimize_out; | |
| 195 | 157 | } |
| 196 | 158 | |
| 197 | - /** | |
| 198 | - * Hides everything between noptimize-comment tags. | |
| 199 | - * | |
| 200 | - * @param string $markup Markup to process. | |
| 201 | - * | |
| 202 | - * @return string | |
| 203 | - */ | |
| 204 | - protected function hide_noptimize( $markup ) | |
| 205 | - { | |
| 206 | - return $this->replace_contents_with_marker_if_exists( | |
| 207 | - 'NOPTIMIZE', | |
| 208 | - '/<!--\s?noptimize\s?-->/', | |
| 209 | - '#<!--\s?noptimize\s?-->.*?<!--\s?/\s?noptimize\s?-->#is', | |
| 210 | - $markup | |
| 211 | - ); | |
| 159 | + protected function hide_iehacks($iehacks_in) { | |
| 160 | + if ( strpos( $iehacks_in, '<!--[if' ) !== false ) { | |
| 161 | + $iehacks_out = preg_replace_callback( | |
| 162 | + '#<!--\[if.*?\[endif\]-->#is', | |
| 163 | + create_function( | |
| 164 | + '$matches', | |
| 165 | + 'return "%%IEHACK".AUTOPTIMIZE_HASH."%%".base64_encode($matches[0])."%%IEHACK%%";' | |
| 166 | + ), | |
| 167 | + $iehacks_in | |
| 168 | + ); | |
| 169 | + } else { | |
| 170 | + $iehacks_out = $iehacks_in; | |
| 171 | + } | |
| 172 | + return $iehacks_out; | |
| 212 | 173 | } |
| 213 | 174 | |
| 214 | - /** | |
| 215 | - * Unhide noptimize-tags. | |
| 216 | - * | |
| 217 | - * @param string $markup Markup to process. | |
| 218 | - * | |
| 219 | - * @return string | |
| 220 | - */ | |
| 221 | - protected function restore_noptimize( $markup ) | |
| 222 | - { | |
| 223 | - return $this->restore_marked_content( 'NOPTIMIZE', $markup ); | |
| 175 | + protected function restore_iehacks($iehacks_in) { | |
| 176 | + if ( strpos( $iehacks_in, '%%IEHACK%%' ) !== false ) { | |
| 177 | + $iehacks_out = preg_replace_callback( | |
| 178 | + '#%%IEHACK'.AUTOPTIMIZE_HASH.'%%(.*?)%%IEHACK%%#is', | |
| 179 | + create_function( | |
| 180 | + '$matches', | |
| 181 | + 'return base64_decode($matches[1]);' | |
| 182 | + ), | |
| 183 | + $iehacks_in | |
| 184 | + ); | |
| 185 | + } else { | |
| 186 | + $iehacks_out=$iehacks_in; | |
| 187 | + } | |
| 188 | + return $iehacks_out; | |
| 224 | 189 | } |
| 225 | 190 | |
| 226 | - /** | |
| 227 | - * Hides "iehacks" content. | |
| 228 | - * | |
| 229 | - * @param string $markup Markup to process. | |
| 230 | - * | |
| 231 | - * @return string | |
| 232 | - */ | |
| 233 | - protected function hide_iehacks( $markup ) | |
| 234 | - { | |
| 235 | - return $this->replace_contents_with_marker_if_exists( | |
| 236 | - 'IEHACK', // Marker name... | |
| 237 | - '<!--[if', // Invalid regex, will fallback to search using strpos()... | |
| 238 | - '#<!--\[if.*?\[endif\]-->#is', // Replacement regex... | |
| 239 | - $markup | |
| 240 | - ); | |
| 191 | + protected function hide_comments($comments_in) { | |
| 192 | + if ( strpos( $comments_in, '<!--' ) !== false ) { | |
| 193 | + $comments_out = preg_replace_callback( | |
| 194 | + '#<!--.*?-->#is', | |
| 195 | + create_function( | |
| 196 | + '$matches', | |
| 197 | + 'return "%%COMMENTS".AUTOPTIMIZE_HASH."%%".base64_encode($matches[0])."%%COMMENTS%%";' | |
| 198 | + ), | |
| 199 | + $comments_in | |
| 200 | + ); | |
| 201 | + } else { | |
| 202 | + $comments_out = $comments_in; | |
| 203 | + } | |
| 204 | + return $comments_out; | |
| 241 | 205 | } |
| 242 | 206 | |
| 243 | - /** | |
| 244 | - * Restores "hidden" iehacks content. | |
| 245 | - * | |
| 246 | - * @param string $markup Markup to process. | |
| 247 | - * | |
| 248 | - * @return string | |
| 249 | - */ | |
| 250 | - protected function restore_iehacks( $markup ) | |
| 251 | - { | |
| 252 | - return $this->restore_marked_content( 'IEHACK', $markup ); | |
| 207 | + protected function restore_comments($comments_in) { | |
| 208 | + if ( strpos( $comments_in, '%%COMMENTS%%' ) !== false ) { | |
| 209 | + $comments_out = preg_replace_callback( | |
| 210 | + '#%%COMMENTS'.AUTOPTIMIZE_HASH.'%%(.*?)%%COMMENTS%%#is', | |
| 211 | + create_function( | |
| 212 | + '$matches', | |
| 213 | + 'return base64_decode($matches[1]);' | |
| 214 | + ), | |
| 215 | + $comments_in | |
| 216 | + ); | |
| 217 | + } else { | |
| 218 | + $comments_out=$comments_in; | |
| 219 | + } | |
| 220 | + return $comments_out; | |
| 253 | 221 | } |
| 254 | 222 | |
| 255 | - /** | |
| 256 | - * "Hides" content within HTML comments using a regex-based replacement | |
| 257 | - * if HTML comment markers are found. | |
| 258 | - * `<!--example-->` becomes `%%COMMENTS%%ZXhhbXBsZQ==%%COMMENTS%%` | |
| 259 | - * | |
| 260 | - * @param string $markup Markup to process. | |
| 261 | - * | |
| 262 | - * @return string | |
| 263 | - */ | |
| 264 | - protected function hide_comments( $markup ) | |
| 265 | - { | |
| 266 | - return $this->replace_contents_with_marker_if_exists( | |
| 267 | - 'COMMENTS', | |
| 268 | - '<!--', | |
| 269 | - '#<!--.*?-->#is', | |
| 270 | - $markup | |
| 271 | - ); | |
| 272 | - } | |
| 223 | + protected function url_replace_cdn( $url ) { | |
| 224 | + // API filter to change base CDN URL | |
| 225 | + $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $this->cdn_url ); | |
| 273 | 226 | |
| 274 | - /** | |
| 275 | - * Restores original HTML comment markers inside a string whose HTML | |
| 276 | - * comments have been "hidden" by using `hide_comments()`. | |
| 277 | - * | |
| 278 | - * @param string $markup Markup to process. | |
| 279 | - * | |
| 280 | - * @return string | |
| 281 | - */ | |
| 282 | - protected function restore_comments( $markup ) | |
| 283 | - { | |
| 284 | - return $this->restore_marked_content( 'COMMENTS', $markup ); | |
| 285 | - } | |
| 286 | - | |
| 287 | - /** | |
| 288 | - * Replaces the given URL with the CDN-version of it when CDN replacement | |
| 289 | - * is supposed to be done. | |
| 290 | - * | |
| 291 | - * @param string $url URL to process. | |
| 292 | - * | |
| 293 | - * @return string | |
| 294 | - */ | |
| 295 | - public function url_replace_cdn( $url ) | |
| 296 | - { | |
| 297 | - // For 2.3 back-compat in which cdn-ing appeared to be automatically | |
| 298 | - // including WP subfolder/subdirectory into account as part of cdn-ing, | |
| 299 | - // even though it might've caused serious troubles in certain edge-cases. | |
| 300 | - $cdn_url = autoptimizeUtils::tweak_cdn_url_if_needed( $this->cdn_url ); | |
| 301 | - | |
| 302 | - // Allows API/filter to further tweak the cdn url... | |
| 303 | - $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $cdn_url ); | |
| 304 | - if ( ! empty( $cdn_url ) ) { | |
| 305 | - $this->debug_log( 'before=' . $url ); | |
| 306 | - | |
| 307 | - // Simple str_replace-based approach fails when $url is protocol-or-host-relative. | |
| 308 | - $is_protocol_relative = autoptimizeUtils::is_protocol_relative( $url ); | |
| 309 | - $is_host_relative = ( ! $is_protocol_relative && ( '/' === $url[0] ) ); | |
| 310 | - $cdn_url = rtrim( $cdn_url, '/' ); | |
| 311 | - | |
| 312 | - if ( $is_host_relative ) { | |
| 313 | - // Prepending host-relative urls with the cdn url. | |
| 314 | - $url = $cdn_url . $url; | |
| 227 | + if ( !empty($cdn_url) ) { | |
| 228 | + // prepend domain-less absolute URL's | |
| 229 | + if ( ( substr( $url, 0, 1 ) === '/' ) && ( substr( $url, 1, 1 ) !== '/' ) ) { | |
| 230 | + $url = rtrim( $cdn_url, '/' ) . $url; | |
| 315 | 231 | } else { |
| 316 | - // Either a protocol-relative or "regular" url, replacing it either way. | |
| 317 | - if ( $is_protocol_relative ) { | |
| 318 | - // Massage $site_url so that simple str_replace() still "works" by | |
| 319 | - // searching for the protocol-relative version of AUTOPTIMIZE_WP_SITE_URL. | |
| 320 | - $site_url = str_replace( array( 'http:', 'https:' ), '', AUTOPTIMIZE_WP_SITE_URL ); | |
| 232 | + // get wordpress base URL | |
| 233 | + $WPSiteBreakdown = parse_url( AUTOPTIMIZE_WP_SITE_URL ); | |
| 234 | + $WPBaseUrl = $WPSiteBreakdown['scheme'] . '://' . $WPSiteBreakdown['host']; | |
| 235 | + if ( ! empty( $WPSiteBreakdown['port'] ) ) { | |
| 236 | + $WPBaseUrl .= ":" . $WPSiteBreakdown['port']; | |
| 237 | + } | |
| 238 | + // replace full url's with scheme | |
| 239 | + $tmp_url = str_replace( $WPBaseUrl, rtrim( $cdn_url, '/' ), $url ); | |
| 240 | + if ( $tmp_url === $url ) { | |
| 241 | + // last attempt; replace scheme-less URL's | |
| 242 | + $url = str_replace( preg_replace( '/https?:/', '', $WPBaseUrl ), rtrim( $cdn_url, '/' ), $url ); | |
| 321 | 243 | } else { |
| 322 | - $site_url = AUTOPTIMIZE_WP_SITE_URL; | |
| 244 | + $url = $tmp_url; | |
| 323 | 245 | } |
| 324 | - $this->debug_log( '`' . $site_url . '` -> `' . $cdn_url . '` in `' . $url . '`' ); | |
| 325 | - $url = str_replace( $site_url, $cdn_url, $url ); | |
| 326 | 246 | } |
| 327 | - | |
| 328 | - $this->debug_log( 'after=' . $url ); | |
| 329 | 247 | } |
| 330 | 248 | |
| 331 | - // Allow API filter to take further care of CDN replacement. | |
| 249 | + // allow API filter to alter URL after CDN replacement | |
| 332 | 250 | $url = apply_filters( 'autoptimize_filter_base_replace_cdn', $url ); |
| 333 | - | |
| 334 | 251 | return $url; |
| 335 | 252 | } |
| 336 | 253 | |
| 337 | - /** | |
| 338 | - * Injects/replaces the given payload markup into `$this->content` | |
| 339 | - * at the specified location. | |
| 340 | - * If the specified tag cannot be found, the payload is appended into | |
| 341 | - * $this->content along with a warning wrapped inside <!--noptimize--> tags. | |
| 342 | - * | |
| 343 | - * @param string $payload Markup to inject. | |
| 344 | - * @param array $where Array specifying the tag name and method of injection. | |
| 345 | - * Index 0 is the tag name (i.e., `</body>`). | |
| 346 | - * Index 1 specifies Ë›'before', 'after' or 'replace'. Defaults to 'before'. | |
| 347 | - * | |
| 348 | - * @return void | |
| 349 | - */ | |
| 350 | - protected function inject_in_html( $payload, $where ) | |
| 351 | - { | |
| 352 | - $warned = false; | |
| 353 | - $position = autoptimizeUtils::strpos( $this->content, $where[0] ); | |
| 354 | - if ( false !== $position ) { | |
| 355 | - // Found the tag, setup content/injection as specified. | |
| 356 | - if ( 'after' === $where[1] ) { | |
| 357 | - $content = $where[0] . $payload; | |
| 358 | - } elseif ( 'replace' === $where[1] ) { | |
| 359 | - $content = $payload; | |
| 254 | + protected function inject_in_html($payload,$replaceTag) { | |
| 255 | + if (strpos($this->content,$replaceTag[0])!== false) { | |
| 256 | + if ($replaceTag[1]==="after") { | |
| 257 | + $replaceBlock=$replaceTag[0].$payload; | |
| 258 | + } else if ($replaceTag[1]==="replace"){ | |
| 259 | + $replaceBlock=$payload; | |
| 360 | 260 | } else { |
| 361 | - $content = $payload . $where[0]; | |
| 261 | + $replaceBlock=$payload.$replaceTag[0]; | |
| 362 | 262 | } |
| 363 | - // Place where specified. | |
| 364 | - $this->content = autoptimizeUtils::substr_replace( | |
| 365 | - $this->content, | |
| 366 | - $content, | |
| 367 | - $position, | |
| 368 | - // Using plain strlen() should be safe here for now, since | |
| 369 | - // we're not searching for multibyte chars here still... | |
| 370 | - strlen( $where[0] ) | |
| 371 | - ); | |
| 263 | + $this->content = substr_replace($this->content,$replaceBlock,strpos($this->content,$replaceTag[0]),strlen($replaceTag[0])); | |
| 372 | 264 | } else { |
| 373 | - // Couldn't find what was specified, just append and add a warning. | |
| 374 | 265 | $this->content .= $payload; |
| 375 | - if ( ! $warned ) { | |
| 376 | - $tag_display = str_replace( array( '<', '>' ), '', $where[0] ); | |
| 377 | - $this->content .= '<!--noptimize--><!-- Autoptimize found a problem with the HTML in your Theme, tag `' . $tag_display . '` missing --><!--/noptimize-->'; | |
| 378 | - $warned = true; | |
| 266 | + if (!$this->tagWarning) { | |
| 267 | + $this->content .= "<!--noptimize--><!-- Autoptimize found a problem with the HTML in your Theme, tag \"".str_replace(array("<",">"),"",$replaceTag[0])."\" missing --><!--/noptimize-->"; | |
| 268 | + $this->tagWarning=true; | |
| 379 | 269 | } |
| 380 | 270 | } |
| 381 | 271 | } |
| 382 | - | |
| 383 | - /** | |
| 384 | - * Returns true if given `$tag` is found in the list of `$removables`. | |
| 385 | - * | |
| 386 | - * @param string $tag Tag to search for. | |
| 387 | - * @param array $removables List of things considered completely removable. | |
| 388 | - * | |
| 389 | - * @return bool | |
| 390 | - */ | |
| 391 | - protected function isremovable( $tag, $removables ) | |
| 392 | - { | |
| 393 | - foreach ( $removables as $match ) { | |
| 394 | - if ( false !== strpos( $tag, $match ) ) { | |
| 272 | + | |
| 273 | + protected function isremovable($tag, $removables) { | |
| 274 | + foreach ($removables as $match) { | |
| 275 | + if (strpos($tag,$match)!==false) { | |
| 395 | 276 | return true; |
| 396 | 277 | } |
| 397 | 278 | } |
| 398 | - | |
| 399 | 279 | return false; |
| 400 | 280 | } |
| 281 | + | |
| 282 | + // inject already minified code in optimized JS/CSS | |
| 283 | + protected function inject_minified($in) { | |
| 284 | + if ( strpos( $in, '%%INJECTLATER%%' ) !== false ) { | |
| 285 | + $out = preg_replace_callback( | |
| 286 | + '#%%INJECTLATER'.AUTOPTIMIZE_HASH.'%%(.*?)%%INJECTLATER%%#is', | |
| 287 | + create_function( | |
| 288 | + '$matches', | |
| 289 | + '$filepath=base64_decode(strtok($matches[1],"|")); | |
| 290 | + $filecontent=file_get_contents($filepath); | |
| 401 | 291 | |
| 402 | - /** | |
| 403 | - * Callback used in `self::inject_minified()`. | |
| 404 | - * | |
| 405 | - * @param array $matches Regex matches. | |
| 406 | - * | |
| 407 | - * @return string | |
| 408 | - */ | |
| 409 | - public function inject_minified_callback( $matches ) | |
| 410 | - { | |
| 411 | - static $conf = null; | |
| 412 | - if ( null === $conf ) { | |
| 413 | - $conf = autoptimizeConfig::instance(); | |
| 414 | - } | |
| 292 | + // remove BOM | |
| 293 | + $filecontent = preg_replace("#\x{EF}\x{BB}\x{BF}#","",$filecontent); | |
| 415 | 294 | |
| 416 | - /** | |
| 417 | - * $matches[1] holds the whole match caught by regex in self::inject_minified(), | |
| 418 | - * so we take that and split the string on `|`. | |
| 419 | - * First element is the filepath, second is the md5 hash of contents | |
| 420 | - * the filepath had when it was being processed. | |
| 421 | - * If we don't have those, we'll bail out early. | |
| 422 | - */ | |
| 423 | - $filepath = null; | |
| 424 | - $filehash = null; | |
| 295 | + // remove comments and blank lines | |
| 296 | + if (substr($filepath,-3,3)===".js") { | |
| 297 | + $filecontent=preg_replace("#^\s*\/\/.*$#Um","",$filecontent); | |
| 298 | + } | |
| 425 | 299 | |
| 426 | - // Grab the parts we need. | |
| 427 | - $parts = explode( '|', $matches[1] ); | |
| 428 | - if ( ! empty( $parts ) ) { | |
| 429 | - $filepath = isset( $parts[0] ) ? base64_decode( $parts[0] ) : null; | |
| 430 | - $filehash = isset( $parts[1] ) ? $parts[1] : null; | |
| 431 | - } | |
| 300 | + $filecontent=preg_replace("#^\s*\/\*[^!].*\*\/\s?#Um","",$filecontent); | |
| 301 | + $filecontent=preg_replace("#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $filecontent); | |
| 432 | 302 | |
| 433 | - // Bail early if something's not right... | |
| 434 | - if ( ! $filepath || ! $filehash ) { | |
| 435 | - return "\n"; | |
| 436 | - } | |
| 303 | + // specific stuff for JS-files | |
| 304 | + if (substr($filepath,-3,3)===".js") { | |
| 305 | + if ((substr($filecontent,-1,1)!==";")&&(substr($filecontent,-1,1)!=="}")) { | |
| 306 | + $filecontent.=";"; | |
| 307 | + } | |
| 437 | 308 | |
| 438 | - $filecontent = file_get_contents( $filepath ); | |
| 309 | + if (get_option("autoptimize_js_trycatch")==="on") { | |
| 310 | + $filecontent="try{".$filecontent."}catch(e){}"; | |
| 311 | + } | |
| 312 | + } else if ((substr($filepath,-4,4)===".css")) { | |
| 313 | + $filecontent=autoptimizeStyles::fixurls($filepath,$filecontent); | |
| 314 | + } else { | |
| 315 | + $filecontent=""; | |
| 316 | + } | |
| 439 | 317 | |
| 440 | - // Some things are differently handled for css/js... | |
| 441 | - $is_js_file = ( '.js' === substr( $filepath, -3, 3 ) ); | |
| 442 | - | |
| 443 | - $is_css_file = false; | |
| 444 | - if ( ! $is_js_file ) { | |
| 445 | - $is_css_file = ( '.css' === substr( $filepath, -4, 4 ) ); | |
| 446 | - } | |
| 447 | - | |
| 448 | - // BOMs being nuked here unconditionally (regardless of where they are)! | |
| 449 | - $filecontent = preg_replace( "#\x{EF}\x{BB}\x{BF}#", '', $filecontent ); | |
| 450 | - | |
| 451 | - // Remove comments and blank lines. | |
| 452 | - if ( $is_js_file ) { | |
| 453 | - $filecontent = preg_replace( '#^\s*\/\/.*$#Um', '', $filecontent ); | |
| 454 | - } | |
| 455 | - | |
| 456 | - // Nuke un-important comments. | |
| 457 | - $filecontent = preg_replace( '#^\s*\/\*[^!].*\*\/\s?#Um', '', $filecontent ); | |
| 458 | - | |
| 459 | - // Normalize newlines. | |
| 460 | - $filecontent = preg_replace( '#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#', "\n", $filecontent ); | |
| 461 | - | |
| 462 | - // JS specifics. | |
| 463 | - if ( $is_js_file ) { | |
| 464 | - // Append a semicolon at the end of js files if it's missing. | |
| 465 | - $last_char = substr( $filecontent, -1, 1 ); | |
| 466 | - if ( ';' !== $last_char && '}' !== $last_char ) { | |
| 467 | - $filecontent .= ';'; | |
| 468 | - } | |
| 469 | - // Check if try/catch should be used. | |
| 470 | - $opt_js_try_catch = $conf->get( 'autoptimize_js_trycatch' ); | |
| 471 | - if ( 'on' === $opt_js_try_catch ) { | |
| 472 | - // It should, wrap in try/catch. | |
| 473 | - $filecontent = 'try{' . $filecontent . '}catch(e){}'; | |
| 474 | - } | |
| 475 | - } elseif ( $is_css_file ) { | |
| 476 | - $filecontent = autoptimizeStyles::fixurls( $filepath, $filecontent ); | |
| 477 | - } else { | |
| 478 | - $filecontent = ''; | |
| 479 | - } | |
| 480 | - | |
| 481 | - // Return modified (or empty!) code/content. | |
| 482 | - return "\n" . $filecontent; | |
| 483 | - } | |
| 484 | - | |
| 485 | - /** | |
| 486 | - * Inject already minified code in optimized JS/CSS. | |
| 487 | - * | |
| 488 | - * @param string $in Markup. | |
| 489 | - * | |
| 490 | - * @return string | |
| 491 | - */ | |
| 492 | - protected function inject_minified( $in ) | |
| 493 | - { | |
| 494 | - $out = $in; | |
| 495 | - if ( false !== strpos( $in, '%%INJECTLATER%%' ) ) { | |
| 496 | - $out = preg_replace_callback( | |
| 497 | - '#\/\*\!%%INJECTLATER' . AUTOPTIMIZE_HASH . '%%(.*?)%%INJECTLATER%%\*\/#is', | |
| 498 | - array( $this, 'inject_minified_callback' ), | |
| 318 | + // return | |
| 319 | + return "\n".$filecontent;' | |
| 320 | + ), | |
| 499 | 321 | $in |
| 500 | 322 | ); |
| 323 | + } else { | |
| 324 | + $out = $in; | |
| 501 | 325 | } |
| 502 | - | |
| 503 | 326 | return $out; |
| 504 | - } | |
| 505 | - | |
| 506 | - /** | |
| 507 | - * Specialized method to create the INJECTLATER marker. | |
| 508 | - * These are somewhat "special", in the sense that they're additionally wrapped | |
| 509 | - * within an "exclamation mark style" comment, so that they're not stripped | |
| 510 | - * out by minifiers. | |
| 511 | - * They also currently contain the hash of the file's contents too (unlike other markers). | |
| 512 | - * | |
| 513 | - * @param string $filepath Filepath. | |
| 514 | - * @param string $hash Hash. | |
| 515 | - * | |
| 516 | - * @return string | |
| 517 | - */ | |
| 518 | - public static function build_injectlater_marker( $filepath, $hash ) | |
| 519 | - { | |
| 520 | - $contents = '/*!' . self::build_marker( 'INJECTLATER', $filepath, $hash ) . '*/'; | |
| 521 | - | |
| 522 | - return $contents; | |
| 523 | - } | |
| 524 | - | |
| 525 | - /** | |
| 526 | - * Creates and returns a `%%`-style named marker which holds | |
| 527 | - * the base64 encoded `$data`. | |
| 528 | - * If `$hash` is provided, it's appended to the base64 encoded string | |
| 529 | - * using `|` as the separator (in order to support building the | |
| 530 | - * somewhat special/different INJECTLATER marker). | |
| 531 | - * | |
| 532 | - * @param string $name Marker name. | |
| 533 | - * @param string $data Marker data which will be base64-encoded. | |
| 534 | - * @param string|null $hash Optional. | |
| 535 | - * | |
| 536 | - * @return string | |
| 537 | - */ | |
| 538 | - public static function build_marker( $name, $data, $hash = null ) | |
| 539 | - { | |
| 540 | - // Start the marker, add the data. | |
| 541 | - $marker = '%%' . $name . AUTOPTIMIZE_HASH . '%%' . base64_encode( $data ); | |
| 542 | - | |
| 543 | - // Add the hash if provided. | |
| 544 | - if ( null !== $hash ) { | |
| 545 | - $marker .= '|' . $hash; | |
| 546 | - } | |
| 547 | - | |
| 548 | - // Close the marker. | |
| 549 | - $marker .= '%%' . $name . '%%'; | |
| 550 | - | |
| 551 | - return $marker; | |
| 552 | - } | |
| 553 | - | |
| 554 | - /** | |
| 555 | - * Searches for `$search` in `$content` (using either `preg_match()` | |
| 556 | - * or `strpos()`, depending on whether `$search` is a valid regex pattern or not). | |
| 557 | - * If something is found, it replaces `$content` using `$re_replace_pattern`, | |
| 558 | - * effectively creating our named markers (`%%{$marker}%%`. | |
| 559 | - * These are then at some point replaced back to their actual/original/modified | |
| 560 | - * contents using `autoptimizeBase::restore_marked_content()`. | |
| 561 | - * | |
| 562 | - * @param string $marker Marker name (without percent characters). | |
| 563 | - * @param string $search A string or full blown regex pattern to search for in $content. Uses `strpos()` or `preg_match()`. | |
| 564 | - * @param string $re_replace_pattern Regex pattern to use when replacing contents. | |
| 565 | - * @param string $content Content to work on. | |
| 566 | - * | |
| 567 | - * @return string | |
| 568 | - */ | |
| 569 | - public static function replace_contents_with_marker_if_exists( $marker, $search, $re_replace_pattern, $content ) | |
| 570 | - { | |
| 571 | - $found = false; | |
| 572 | - | |
| 573 | - $is_regex = autoptimizeUtils::str_is_valid_regex( $search ); | |
| 574 | - if ( $is_regex ) { | |
| 575 | - $found = preg_match( $search, $content ); | |
| 576 | - } else { | |
| 577 | - $found = ( false !== strpos( $content, $search ) ); | |
| 578 | - } | |
| 579 | - | |
| 580 | - if ( $found ) { | |
| 581 | - $content = preg_replace_callback( | |
| 582 | - $re_replace_pattern, | |
| 583 | - function( $matches ) use ( $marker ) { | |
| 584 | - return autoptimizeBase::build_marker( $marker, $matches[0] ); | |
| 585 | - }, | |
| 586 | - $content | |
| 587 | - ); | |
| 588 | - } | |
| 589 | - | |
| 590 | - return $content; | |
| 591 | - } | |
| 592 | - | |
| 593 | - /** | |
| 594 | - * Complements `autoptimizeBase::replace_contents_with_marker_if_exists()`. | |
| 595 | - * | |
| 596 | - * @param string $marker Marker. | |
| 597 | - * @param string $content Markup. | |
| 598 | - * | |
| 599 | - * @return string | |
| 600 | - */ | |
| 601 | - public static function restore_marked_content( $marker, $content ) | |
| 602 | - { | |
| 603 | - if ( false !== strpos( $content, $marker ) ) { | |
| 604 | - $content = preg_replace_callback( | |
| 605 | - '#%%' . $marker . AUTOPTIMIZE_HASH . '%%(.*?)%%' . $marker . '%%#is', | |
| 606 | - function ( $matches ) { | |
| 607 | - return base64_decode( $matches[1] ); | |
| 608 | - }, | |
| 609 | - $content | |
| 610 | - ); | |
| 611 | - } | |
| 612 | - | |
| 613 | - return $content; | |
| 614 | - } | |
| 615 | - | |
| 616 | - /** | |
| 617 | - * Logs given `$data` for debugging purposes (when debug logging is on). | |
| 618 | - * | |
| 619 | - * @param mixed $data Data to log. | |
| 620 | - * | |
| 621 | - * @return void | |
| 622 | - */ | |
| 623 | - protected function debug_log( $data ) | |
| 624 | - { | |
| 625 | - if ( ! isset( $this->debug_log ) || ! $this->debug_log ) { | |
| 626 | - return; | |
| 627 | - } | |
| 628 | - | |
| 629 | - if ( ! is_string( $data ) && ! is_resource( $data ) ) { | |
| 630 | - $data = var_export( $data, true ); | |
| 631 | - } | |
| 632 | - | |
| 633 | - error_log( $data ); | |
| 634 | - } | |
| 635 | - | |
| 636 | - /** | |
| 637 | - * Checks if a single local css/js file can be minified and returns source if so. | |
| 638 | - * | |
| 639 | - * @param string $filepath Filepath. | |
| 640 | - * | |
| 641 | - * @return bool|string to be minified code or false. | |
| 642 | - */ | |
| 643 | - protected function prepare_minify_single( $filepath ) | |
| 644 | - { | |
| 645 | - // Decide what we're dealing with, return false if we don't know. | |
| 646 | - if ( autoptimizeUtils::str_ends_in( $filepath, '.js' ) ) { | |
| 647 | - $type = 'js'; | |
| 648 | - } elseif ( autoptimizeUtils::str_ends_in( $filepath, '.css' ) ) { | |
| 649 | - $type = 'css'; | |
| 650 | - } else { | |
| 651 | - return false; | |
| 652 | - } | |
| 653 | - | |
| 654 | - // Bail if it looks like its already minifed (by having -min or .min | |
| 655 | - // in filename) or if it looks like WP jquery.js (which is minified). | |
| 656 | - $minified_variants = array( | |
| 657 | - '-min.' . $type, | |
| 658 | - '.min.' . $type, | |
| 659 | - 'js/jquery/jquery.js', | |
| 660 | - ); | |
| 661 | - foreach ( $minified_variants as $ending ) { | |
| 662 | - if ( autoptimizeUtils::str_ends_in( $filepath, $ending ) ) { | |
| 663 | - return false; | |
| 664 | - } | |
| 665 | - } | |
| 666 | - | |
| 667 | - // Get file contents, bail if empty. | |
| 668 | - $contents = file_get_contents( $filepath ); | |
| 669 | - | |
| 670 | - return $contents; | |
| 671 | - } | |
| 672 | - | |
| 673 | - /** | |
| 674 | - * Given an autoptimizeCache instance returns the (maybe cdn-ed) url of | |
| 675 | - * the cached file. | |
| 676 | - * | |
| 677 | - * @param autoptimizeCache $cache autoptimizeCache instance. | |
| 678 | - * | |
| 679 | - * @return string | |
| 680 | - */ | |
| 681 | - protected function build_minify_single_url( autoptimizeCache $cache ) | |
| 682 | - { | |
| 683 | - $url = AUTOPTIMIZE_CACHE_URL . $cache->getname(); | |
| 684 | - | |
| 685 | - // CDN-replace the resulting URL if needed... | |
| 686 | - $url = $this->url_replace_cdn( $url ); | |
| 687 | - | |
| 688 | - return $url; | |
| 689 | 327 | } |
| 690 | 328 | } |