Issues (280)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

classes/autoptimizeBase.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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