PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 2.1
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v2.1
trunk 1.0 1.0.1 1.1 1.1.1 1.1.2 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1 2.1.1 2.1.2 2.2 2.2.1 All 69 releases
powered-cache / includes / file-optimizer.php

file-optimizer.php in Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score 2.1, at includes/file-optimizer.php

345 lines 9.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Ported from https://github.com/Automattic/nginx-http-concat
4 *
5 * Concatenation script inspired by Nginx's ngx_http_concat and Apache's modconcat modules.
6 *
7 * It follows the same pattern for enabling the concatenation. It uses two ?, like this:
8 * http://example.com/??style1.css,style2.css,foo/style3.css
9 *
10 * If a third ? is present it's treated as version string. Like this:
11 * http://example.com/??style1.css,style2.css,foo/style3.css?v=102234
12 *
13 * It will also replace the relative paths in CSS files with absolute paths.
14 *
15 * @package PoweredCache
16 */
17
18 namespace PoweredCache\FileOptimizer;
19
20 // phpcs:disable
21
22 use PoweredCache\Dependencies\MatthiasMullie\Minify\CSS;
23 use PoweredCache\Dependencies\MatthiasMullie\Minify\JS;
24
25 /**
26 * PSR-4-ish autoloading
27 *
28 * @since 2.0
29 */
30 spl_autoload_register(
31 function ( $class ) {
32 // project-specific namespace prefix.
33 $prefix = 'PoweredCache\\';
34
35 // base directory for the namespace prefix.
36 $base_dir = __DIR__ . '/classes/';
37
38 // does the class use the namespace prefix?
39 $len = strlen( $prefix );
40
41 if ( strncmp( $prefix, $class, $len ) !== 0 ) {
42 return;
43 }
44
45 $relative_class = substr( $class, $len );
46
47 $file = $base_dir . str_replace( '\\', '/', $relative_class ) . '.php';
48
49 // if the file exists, require it.
50 if ( file_exists( $file ) ) {
51 require $file;
52 }
53 }
54 );
55
56 /* Config */
57 $concat_max_files = 150;
58 $concat_unique = true;
59 $concat_types = array(
60 'css' => 'text/css',
61 'js' => 'application/javascript',
62 );
63
64 /*
65 Constants */
66 // By default determine the document root from this scripts path in the plugins dir (you can hardcode this define)
67 define( 'CONCAT_FILES_ROOT', substr( dirname( __DIR__ ), 0, strpos( dirname( __DIR__ ), '/wp-content' ) ) );
68 define( 'POWERED_CACHE_FO_CACHE_DIR', CONCAT_FILES_ROOT . '/wp-content/cache/min/' );
69
70 if ( ! file_exists( POWERED_CACHE_FO_CACHE_DIR ) ) {
71 mkdir( POWERED_CACHE_FO_CACHE_DIR, 0775, true );
72 }
73
74 function concat_http_status_exit( $status ) {
75 switch ( $status ) {
76 case 200:
77 $text = 'OK';
78 break;
79 case 400:
80 $text = 'Bad Request';
81 break;
82 case 403:
83 $text = 'Forbidden';
84 break;
85 case 404:
86 $text = 'Not found';
87 break;
88 case 500:
89 $text = 'Internal Server Error';
90 break;
91 default:
92 $text = '';
93 }
94
95 $protocol = $_SERVER['SERVER_PROTOCOL'];
96 if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol ) {
97 $protocol = 'HTTP/1.0';
98 }
99
100 @header( "$protocol $status $text", true, $status );
101 exit();
102 }
103
104 function concat_get_mtype( $file ) {
105 global $concat_types;
106
107 $lastdot_pos = strrpos( $file, '.' );
108 if ( false === $lastdot_pos ) {
109 return false;
110 }
111
112 $ext = substr( $file, $lastdot_pos + 1 );
113
114 return isset( $concat_types[ $ext ] ) ? $concat_types[ $ext ] : false;
115 }
116
117 function concat_get_path( $uri ) {
118 if ( ! strlen( $uri ) ) {
119 concat_http_status_exit( 400 );
120 }
121
122 if ( false !== strpos( $uri, '..' ) || false !== strpos( $uri, "\0" ) ) {
123 concat_http_status_exit( 400 );
124 }
125
126 return CONCAT_FILES_ROOT . ( '/' != $uri[0] ? '/' : '' ) . $uri;
127 }
128
129 function relative_path_replace( $buf, $dirpath ) {
130 // url(relative/path/to/file) -> url(/absolute/and/not/relative/path/to/file)
131 $buf = preg_replace(
132 '/(:?\s*url\s*\()\s*(?:\'|")?\s*([^\/\'"\s\)](?:(?<!data:|http:|https:|[\(\'"]#|%23).)*)[\'"\s]*\)/isU',
133 '$1' . ( '/' === $dirpath ? '/' : $dirpath . '/' ) . '$2)',
134 $buf
135 );
136
137 return $buf;
138 }
139
140 /* Main() */
141 if ( ! in_array( $_SERVER['REQUEST_METHOD'], array( 'GET', 'HEAD' ) ) ) {
142 concat_http_status_exit( 400 );
143 }
144
145 // /_static/??/foo/bar.css,/foo1/bar/baz.css?m=293847g
146 // or
147 // /_static/??-eJzTT8vP109KLNJLLi7W0QdyDEE8IK4CiVjn2hpZGluYmKcDABRMDPM=
148 $args = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY );
149 if ( ! $args || false === strpos( $args, '?' ) ) {
150 concat_http_status_exit( 400 );
151 }
152
153 $args = substr( $args, strpos( $args, '?' ) + 1 );
154
155 // /foo/bar.css,/foo1/bar/baz.css?m=293847g
156 // or
157 // -eJzTT8vP109KLNJLLi7W0QdyDEE8IK4CiVjn2hpZGluYmKcDABRMDPM=
158 if ( '-' == $args[0] ) {
159 $args = @gzuncompress( base64_decode( substr( $args, 1 ) ) );
160
161 // Invalid data, abort!
162 if ( false === $args ) {
163 concat_http_status_exit( 400 );
164 }
165 }
166
167 // /foo/bar.css,/foo1/bar/baz.css?m=293847g
168 $version_string_pos = strpos( $args, '?' );
169 if ( false !== $version_string_pos ) {
170 $args = substr( $args, 0, $version_string_pos );
171 }
172
173 // /foo/bar.css,/foo1/bar/baz.css
174 $args = explode( ',', $args );
175 if ( ! $args ) {
176 concat_http_status_exit( 400 );
177 }
178
179 // array( '/foo/bar.css', '/foo1/bar/baz.css' )
180 if ( 0 == count( $args ) || count( $args ) > $concat_max_files ) {
181 concat_http_status_exit( 400 );
182 }
183
184 // If we're in a subdirectory context, use that as the root.
185 // We can't assume that the root serves the same content as the subdir.
186 $subdir_path_prefix = '';
187 $request_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
188 $_static_index = strpos( $request_path, '/_static/' );
189 if ( $_static_index > 0 ) {
190 $subdir_path_prefix = substr( $request_path, 0, $_static_index );
191 }
192 unset( $request_path, $_static_index );
193
194 $last_modified = 0;
195 $pre_output = '';
196 $output = '';
197
198 $do_minify = (bool) stripos( $_SERVER['REQUEST_URI'], 'minify=1' );
199 $hash = sha1( $_SERVER['REQUEST_URI'] );
200 $latest_file = end( $args );
201
202 $cache_file_name = POWERED_CACHE_FO_CACHE_DIR . $hash;
203 if ( 'application/javascript' == concat_get_mtype( $latest_file ) ) {
204 $cache_file_name .= '.js';
205 } elseif ( 'text/css' == concat_get_mtype( $latest_file ) ) {
206 $cache_file_name .= '.css';
207 }
208
209 if ( file_exists( $cache_file_name ) ) {
210 $buf = @file_get_contents( $cache_file_name );
211 if ( $buf ) {
212 $stat = stat( $cache_file_name );
213
214 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $stat['mtime'] ) . ' GMT' );
215 header( 'Content-Length: ' . ( strlen( $pre_output ) + strlen( $buf ) ) );
216 header( 'Content-Type: ' . concat_get_mtype( $latest_file ) );
217
218 echo $pre_output . $buf;
219 exit;
220 }
221 }
222
223 foreach ( $args as $uri ) {
224
225 $fullpath = concat_get_path( $uri );
226
227 if ( ! file_exists( $fullpath ) ) {
228 concat_http_status_exit( 404 );
229 }
230
231 $mime_type = concat_get_mtype( $fullpath );
232 if ( ! in_array( $mime_type, $concat_types ) ) {
233 concat_http_status_exit( 400 );
234 }
235
236 if ( $concat_unique ) {
237 if ( ! isset( $last_mime_type ) ) {
238 $last_mime_type = $mime_type;
239 }
240
241 if ( $last_mime_type != $mime_type ) {
242 concat_http_status_exit( 400 );
243 }
244 }
245
246 $stat = stat( $fullpath );
247 if ( false === $stat ) {
248 concat_http_status_exit( 500 );
249 }
250
251 if ( $stat['mtime'] > $last_modified ) {
252 $last_modified = $stat['mtime'];
253 }
254
255 $buf = file_get_contents( $fullpath );
256 if ( false === $buf ) {
257 concat_http_status_exit( 500 );
258 }
259
260 if ( 'text/css' == $mime_type ) {
261 $dirpath = $subdir_path_prefix . dirname( $uri );
262
263 // url(relative/path/to/file) -> url(/absolute/and/not/relative/path/to/file)
264 $buf = relative_path_replace( $buf, $dirpath );
265
266 // AlphaImageLoader(...src='relative/path/to/file'...) -> AlphaImageLoader(...src='/absolute/path/to/file'...)
267 $buf = preg_replace(
268 '/(Microsoft.AlphaImageLoader\s*\([^\)]*src=(?:\'|")?)([^\/\'"\s\)](?:(?<!http:|https:).)*)\)/isU',
269 '$1' . ( $dirpath == '/' ? '/' : $dirpath . '/' ) . '$2)',
270 $buf
271 );
272
273 // The @charset rules must be on top of the output
274 if ( 0 === strpos( $buf, '@charset' ) ) {
275 preg_replace_callback(
276 '/(?P<charset_rule>@charset\s+[\'"][^\'"]+[\'"];)/i',
277 function ( $match ) {
278 global $pre_output;
279
280 if ( 0 === strpos( $pre_output, '@charset' ) ) {
281 return '';
282 }
283
284 $pre_output = $match[0] . "\n" . $pre_output;
285
286 return '';
287 },
288 $buf
289 );
290 }
291
292 // Move the @import rules on top of the concatenated output.
293 // Only @charset rule are allowed before them.
294 if ( false !== strpos( $buf, '@import' ) ) {
295 $buf = preg_replace_callback(
296 '/(?P<pre_path>@import\s+(?:url\s*\()?[\'"\s]*)(?P<path>[^\'"\s](?:https?:\/\/.+\/?)?.+?)(?P<post_path>[\'"\s\)]*;)/i',
297 function ( $match ) use ( $dirpath ) {
298 global $pre_output;
299
300 if ( 0 !== strpos( $match['path'], 'http' ) && '/' != $match['path'][0] ) {
301 $pre_output .= $match['pre_path'] . ( $dirpath == '/' ? '/' : $dirpath . '/' ) .
302 $match['path'] . $match['post_path'] . "\n";
303 } else {
304 $pre_output .= $match[0] . "\n";
305 }
306
307 return '';
308 },
309 $buf
310 );
311 }
312 }
313
314 if ( 'application/javascript' == $mime_type ) {
315 $output .= "$buf;\n";
316 } else {
317 $output .= "$buf";
318 }
319 }
320
321 if ( $do_minify && false === stripos( $uri, '.min' ) ) {
322 if ( 'application/javascript' == $mime_type ) {
323 $js_minify = new JS( $output );
324 $output = $js_minify->minify();
325 } elseif ( 'text/css' == $mime_type ) {
326 $css_minify = new CSS( $output );
327 $output = $css_minify->minify();
328 }
329 }
330
331 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $last_modified ) . ' GMT' );
332 header( 'Content-Length: ' . ( strlen( $pre_output ) + strlen( $output ) ) );
333 header( "Content-Type: $mime_type" );
334
335 echo $pre_output . $output;
336
337 $cache_file_name = POWERED_CACHE_FO_CACHE_DIR . $hash;
338 if ( 'application/javascript' == $mime_type ) {
339 $cache_file_name .= '.js';
340 } elseif ( 'text/css' == $mime_type ) {
341 $cache_file_name .= '.css';
342 }
343
344 file_put_contents( $cache_file_name, $pre_output . $output );
345