PluginProbe ʕ •ᴥ•ʔ
LiteSpeed Cache / 7.6.1
LiteSpeed Cache v7.6.1
trunk 1.0.15 1.9.1.1 2.9.9.2 3.6.4 4.6 5.7.0.1 6.5.4 7.0.0.1 7.0.1 7.1 7.2 7.3 7.3.0.1 7.4 7.5 7.5.0.1 7.6 7.6.1 7.6.2 7.7 7.8 7.8.0.1 7.8.1
litespeed-cache / src / optimizer.cls.php
litespeed-cache / src Last commit date
cdn 7 months ago data_structure 7 months ago activation.cls.php 7 months ago admin-display.cls.php 7 months ago admin-settings.cls.php 7 months ago admin.cls.php 7 months ago api.cls.php 7 months ago avatar.cls.php 7 months ago base.cls.php 7 months ago cdn.cls.php 7 months ago cloud.cls.php 7 months ago conf.cls.php 7 months ago control.cls.php 7 months ago core.cls.php 7 months ago crawler-map.cls.php 7 months ago crawler.cls.php 7 months ago css.cls.php 7 months ago data.cls.php 7 months ago data.upgrade.func.php 7 months ago db-optm.cls.php 7 months ago debug2.cls.php 7 months ago doc.cls.php 7 months ago error.cls.php 7 months ago esi.cls.php 7 months ago file.cls.php 7 months ago gui.cls.php 7 months ago health.cls.php 7 months ago htaccess.cls.php 7 months ago img-optm.cls.php 7 months ago import.cls.php 7 months ago import.preset.cls.php 7 months ago lang.cls.php 7 months ago localization.cls.php 7 months ago media.cls.php 7 months ago metabox.cls.php 7 months ago object-cache-wp.cls.php 7 months ago object-cache.cls.php 7 months ago object.lib.php 7 months ago optimize.cls.php 7 months ago optimizer.cls.php 7 months ago placeholder.cls.php 7 months ago purge.cls.php 7 months ago report.cls.php 7 months ago rest.cls.php 7 months ago root.cls.php 7 months ago router.cls.php 7 months ago str.cls.php 7 months ago tag.cls.php 7 months ago task.cls.php 7 months ago tool.cls.php 7 months ago ucss.cls.php 7 months ago utility.cls.php 7 months ago vary.cls.php 7 months ago vpi.cls.php 7 months ago
optimizer.cls.php
354 lines
1 <?php
2 // phpcs:ignoreFile
3
4 /**
5 * The optimize4 class.
6 *
7 * @since 1.9
8 * @package LiteSpeed
9 */
10
11 namespace LiteSpeed;
12
13 defined('WPINC') || exit();
14
15 class Optimizer extends Root {
16
17 private $_conf_css_font_display;
18
19 /**
20 * Init optimizer
21 *
22 * @since 1.9
23 */
24 public function __construct() {
25 $this->_conf_css_font_display = $this->conf(Base::O_OPTM_CSS_FONT_DISPLAY);
26 }
27
28 /**
29 * Run HTML minify process and return final content
30 *
31 * @since 1.9
32 * @access public
33 */
34 public function html_min( $content, $force_inline_minify = false ) {
35 if (!apply_filters('litespeed_html_min', true)) {
36 Debug2::debug2('[Optmer] html_min bypassed via litespeed_html_min filter');
37 return $content;
38 }
39
40 $options = array();
41
42 if ($force_inline_minify) {
43 $options['jsMinifier'] = __CLASS__ . '::minify_js';
44 }
45
46 $skip_comments = $this->conf(Base::O_OPTM_HTML_SKIP_COMMENTS);
47 if ($skip_comments) {
48 $options['skipComments'] = $skip_comments;
49 }
50
51 /**
52 * Added exception capture when minify
53 *
54 * @since 2.2.3
55 */
56 try {
57 $obj = new Lib\HTML_MIN($content, $options);
58 $content_final = $obj->process();
59 // check if content from minification is empty
60 if ($content_final == '') {
61 Debug2::debug('Failed to minify HTML: HTML minification resulted in empty HTML');
62 return $content;
63 }
64 if (!defined('LSCACHE_ESI_SILENCE')) {
65 $content_final .= "\n" . '<!-- Page optimized by LiteSpeed Cache @' . date('Y-m-d H:i:s', time() + LITESPEED_TIME_OFFSET) . ' -->';
66 }
67 return $content_final;
68 } catch (\Exception $e) {
69 Debug2::debug('******[Optmer] html_min failed: ' . $e->getMessage());
70 error_log('****** LiteSpeed Optimizer html_min failed: ' . $e->getMessage());
71 return $content;
72 }
73 }
74
75 /**
76 * Run minify process and save content
77 *
78 * @since 1.9
79 * @access public
80 */
81 public function serve( $request_url, $file_type, $minify, $src_list ) {
82 // Try Unique CSS
83 if ($file_type == 'css') {
84 $content = false;
85 if (defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_OPTM_UCSS)) {
86 $filename = $this->cls('UCSS')->load($request_url);
87
88 if ($filename) {
89 return array( $filename, 'ucss' );
90 }
91 }
92 }
93
94 // Before generated, don't know the contented hash filename yet, so used url hash as tmp filename
95 $file_path_prefix = $this->_build_filepath_prefix($file_type);
96
97 $url_tag = $request_url;
98 $url_tag_for_file = md5($request_url);
99 if (is_404()) {
100 $url_tag_for_file = $url_tag = '404';
101 } elseif ($file_type == 'css' && apply_filters('litespeed_ucss_per_pagetype', false)) {
102 $url_tag_for_file = $url_tag = Utility::page_type();
103 }
104
105 $static_file = LITESPEED_STATIC_DIR . $file_path_prefix . $url_tag_for_file . '.' . $file_type;
106
107 // Create tmp file to avoid conflict
108 $tmp_static_file = $static_file . '.tmp';
109 if (file_exists($tmp_static_file) && time() - filemtime($tmp_static_file) <= 600) {
110 // some other request is generating
111 return false;
112 }
113 // File::save( $tmp_static_file, '/* ' . ( is_404() ? '404' : $request_url ) . ' */', true ); // Can't use this bcos this will get filecon md5 changed
114 File::save($tmp_static_file, '', true);
115
116 // Load content
117 $real_files = array();
118 foreach ($src_list as $src_info) {
119 $is_min = false;
120 if (!empty($src_info['inl'])) {
121 // Load inline
122 $content = $src_info['src'];
123 } else {
124 // Load file
125 $content = $this->load_file($src_info['src'], $file_type);
126
127 if (!$content) {
128 continue;
129 }
130
131 $is_min = $this->is_min($src_info['src']);
132 }
133 $content = $this->optm_snippet($content, $file_type, $minify && !$is_min, $src_info['src'], !empty($src_info['media']) ? $src_info['media'] : false);
134 // Write to file
135 File::save($tmp_static_file, $content, true, true);
136 }
137
138 // if CSS - run the minification on the saved file.
139 // Will move imports to the top of file and remove extra spaces.
140 if ($file_type == 'css') {
141 $obj = new Lib\CSS_JS_MIN\Minify\CSS();
142 $file_content_combined = $obj->moveImportsToTop(File::read($tmp_static_file));
143
144 File::save($tmp_static_file, $file_content_combined);
145 }
146
147 // validate md5
148 $filecon_md5 = md5_file($tmp_static_file);
149
150 $final_file_path = $file_path_prefix . $filecon_md5 . '.' . $file_type;
151 $realfile = LITESPEED_STATIC_DIR . $final_file_path;
152 if (!file_exists($realfile)) {
153 rename($tmp_static_file, $realfile);
154 Debug2::debug2('[Optmer] Saved static file [path] ' . $realfile);
155 } else {
156 unlink($tmp_static_file);
157 }
158
159 $vary = $this->cls('Vary')->finalize_full_varies();
160 Debug2::debug2("[Optmer] Save URL to file for [file_type] $file_type [file] $filecon_md5 [vary] $vary ");
161 $this->cls('Data')->save_url($url_tag, $vary, $file_type, $filecon_md5, dirname($realfile));
162
163 return array( $filecon_md5 . '.' . $file_type, $file_type );
164 }
165
166 /**
167 * Load a single file
168 *
169 * @since 4.0
170 */
171 public function optm_snippet( $content, $file_type, $minify, $src, $media = false ) {
172 // CSS related features
173 if ($file_type == 'css') {
174 // Font optimize
175 if ($this->_conf_css_font_display) {
176 $content = preg_replace('#(@font\-face\s*\{)#isU', '${1}font-display:swap;', $content);
177 }
178
179 $content = preg_replace('/@charset[^;]+;\\s*/', '', $content);
180
181 if ($media) {
182 $content = '@media ' . $media . '{' . $content . "\n}";
183 }
184
185 if ($minify) {
186 $content = self::minify_css($content);
187 }
188
189 $content = $this->cls('CDN')->finalize($content);
190
191 if ((defined('LITESPEED_GUEST_OPTM') || $this->conf(Base::O_IMG_OPTM_WEBP)) && $this->cls('Media')->webp_support()) {
192 $content = $this->cls('Media')->replace_background_webp($content);
193 }
194 } else {
195 if ($minify) {
196 $content = self::minify_js($content);
197 } else {
198 $content = $this->_null_minifier($content);
199 }
200
201 $content .= "\n;";
202 }
203
204 // Add filter
205 $content = apply_filters('litespeed_optm_cssjs', $content, $file_type, $src);
206
207 return $content;
208 }
209
210 /**
211 * Load remote resource from cache if existed
212 *
213 * @since 4.7
214 */
215 private function load_cached_file( $url, $file_type ) {
216 $file_path_prefix = $this->_build_filepath_prefix($file_type);
217 $folder_name = LITESPEED_STATIC_DIR . $file_path_prefix;
218 $to_be_deleted_folder = $folder_name . date('Ymd', strtotime('-2 days'));
219 if (file_exists($to_be_deleted_folder)) {
220 Debug2::debug('[Optimizer] ❌ Clearing folder [name] ' . $to_be_deleted_folder);
221 File::rrmdir($to_be_deleted_folder);
222 }
223
224 $today_file = $folder_name . date('Ymd') . '/' . md5($url);
225 if (file_exists($today_file)) {
226 return File::read($today_file);
227 }
228
229 // Write file
230 $res = wp_safe_remote_get($url);
231 $res_code = wp_remote_retrieve_response_code($res);
232 if (is_wp_error($res) || $res_code != 200) {
233 Debug2::debug2('[Optimizer] ❌ Load Remote error [code] ' . $res_code);
234 return false;
235 }
236 $con = wp_remote_retrieve_body($res);
237 if (!$con) {
238 return false;
239 }
240
241 Debug2::debug('[Optimizer] �
242 Save remote file to cache [name] ' . $today_file);
243 File::save($today_file, $con, true);
244
245 return $con;
246 }
247
248 /**
249 * Load remote/local resource
250 *
251 * @since 3.5
252 */
253 public function load_file( $src, $file_type = 'css' ) {
254 $real_file = Utility::is_internal_file($src);
255 $postfix = pathinfo(parse_url($src, PHP_URL_PATH), PATHINFO_EXTENSION);
256 if (!$real_file || $postfix != $file_type) {
257 Debug2::debug2('[CSS] Load Remote [' . $file_type . '] ' . $src);
258 $this_url = substr($src, 0, 2) == '//' ? set_url_scheme($src) : $src;
259 $con = $this->load_cached_file($this_url, $file_type);
260
261 if ($file_type == 'css') {
262 $dirname = dirname($this_url) . '/';
263
264 $con = Lib\UriRewriter::prepend($con, $dirname);
265 }
266 } else {
267 Debug2::debug2('[CSS] Load local [' . $file_type . '] ' . $real_file[0]);
268 $con = File::read($real_file[0]);
269
270 if ($file_type == 'css') {
271 $dirname = dirname($real_file[0]);
272
273 $con = Lib\UriRewriter::rewrite($con, $dirname);
274 }
275 }
276
277 return $con;
278 }
279
280 /**
281 * Minify CSS
282 *
283 * @since 2.2.3
284 * @access private
285 */
286 public static function minify_css( $data ) {
287 try {
288 $obj = new Lib\CSS_JS_MIN\Minify\CSS();
289 $obj->add($data);
290
291 return $obj->minify();
292 } catch (\Exception $e) {
293 Debug2::debug('******[Optmer] minify_css failed: ' . $e->getMessage());
294 error_log('****** LiteSpeed Optimizer minify_css failed: ' . $e->getMessage());
295 return $data;
296 }
297 }
298
299 /**
300 * Minify JS
301 *
302 * Added exception capture when minify
303 *
304 * @since 2.2.3
305 * @access private
306 */
307 public static function minify_js( $data, $js_type = '' ) {
308 // For inline JS optimize, need to check if it's js type
309 if ($js_type) {
310 preg_match('#type=([\'"])(.+)\g{1}#isU', $js_type, $matches);
311 if ($matches && $matches[2] != 'text/javascript') {
312 Debug2::debug('******[Optmer] minify_js bypass due to type: ' . $matches[2]);
313 return $data;
314 }
315 }
316
317 try {
318 $obj = new Lib\CSS_JS_MIN\Minify\JS();
319 $obj->add($data);
320
321 return $obj->minify();
322 } catch (\Exception $e) {
323 Debug2::debug('******[Optmer] minify_js failed: ' . $e->getMessage());
324 // error_log( '****** LiteSpeed Optimizer minify_js failed: ' . $e->getMessage() );
325 return $data;
326 }
327 }
328
329 /**
330 * Basic minifier
331 *
332 * @access private
333 */
334 private function _null_minifier( $content ) {
335 $content = str_replace("\r\n", "\n", $content);
336
337 return trim($content);
338 }
339
340 /**
341 * Check if the file is already min file
342 *
343 * @since 1.9
344 */
345 public function is_min( $filename ) {
346 $basename = basename($filename);
347 if (preg_match('/[-\.]min\.(?:[a-zA-Z]+)$/i', $basename)) {
348 return true;
349 }
350
351 return false;
352 }
353 }
354