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