PluginProbe
Autoptimize / 2.1.2
Autoptimize v2.1.2
2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 All 107 releases
← All changes | classes/autoptimizeBase.php +245 -607 2.7.72.1.2 View file →
@@ -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 }