Completed
Push — master ( c92350...ab0c5b )
by frank
01:42
created

autoptimizeBase::str_ends_in()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
1
<?php
2
/**
3
 * Base class other (more-specific) classes inherit from.
4
 */
5
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
     */
17
    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
    {
35
        $this->content = $content;
36
    }
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
     */
52
    abstract public function minify();
53
54
    /**
55
     * Caches the things.
56
     *
57
     * @return void
58
     */
59
    abstract public function cache();
60
61
    /**
62
     * Returns the content
63
     *
64
     * @return string
65
     */
66
    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 );
82
        }
83
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;
92
            } else {
93
                $url = 'http:' . $url;
94
            }
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;
98
            } else {
99
                $url = AUTOPTIMIZE_WP_SITE_URL . autoptimizeUtils::path_canonicalize( $url );
100
            }
101
        }
102
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
             */
116
            $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 );
121
            }
122
123
            if ( ! empty( $this->cdn_url ) ) {
124
                $multidomains[] = parse_url( $this->cdn_url, PHP_URL_HOST );
125
            }
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 );
132
                } else {
133
                    return false;
134
                }
135
            } else {
136
                return false;
137
            }
138
        }
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 );
146
        }
147
148
        $tmp_url = preg_replace( '/https?:/', '', $url );
149
        $path    = str_replace( $tmp_ao_root, '', $tmp_url );
150
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;
156
        }
157
158
        // Prepend with WP_ROOT_DIR to have full path to file.
159
        $path = str_replace( '//', '/', WP_ROOT_DIR . $path );
160
161
        // Final check: does file exist and is it readable?
162
        if ( file_exists( $path ) && is_file( $path ) && is_readable( $path ) ) {
163
            return $path;
164
        } else {
165
            return false;
166
        }
167
    }
168
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;
184
        }
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;
192
        }
193
194
        return $hostname;
195
    }
196
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
        );
212
    }
213
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 );
224
    }
225
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
        );
241
    }
242
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 );
253
    }
254
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
    }
273
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;
315
            } 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 );
321
                } else {
322
                    $site_url = AUTOPTIMIZE_WP_SITE_URL;
323
                }
324
                $this->debug_log( '`' . $site_url . '` -> `' . $cdn_url . '` in `' . $url . '`' );
325
                $url = str_replace( $site_url, $cdn_url, $url );
326
            }
327
328
            $this->debug_log( 'after=' . $url );
329
        }
330
331
        // Allow API filter to take further care of CDN replacement.
332
        $url = apply_filters( 'autoptimize_filter_base_replace_cdn', $url );
333
334
        return $url;
335
    }
336
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;
360
            } else {
361
                $content = $payload . $where[0];
362
            }
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
            );
372
        } else {
373
            // Couldn't find what was specified, just append and add a warning.
374
            $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;
0 ignored issues
show
Unused Code introduced by
$warned is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
379
            }
380
        }
381
    }
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 ) ) {
395
                return true;
396
            }
397
        }
398
399
        return false;
400
    }
401
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
        }
415
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;
425
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
        }
432
433
        // Bail early if something's not right...
434
        if ( ! $filepath || ! $filehash ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filepath of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
435
            return "\n";
436
        }
437
438
        $filecontent = file_get_contents( $filepath );
439
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' ),
499
                $in
500
            );
501
        }
502
503
        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;
0 ignored issues
show
Unused Code introduced by
$found is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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
    }
690
}
691