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