PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.3
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.3
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / includes / class-wp-optimize-utils.php

class-wp-optimize-utils.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.5.3, at includes/class-wp-optimize-utils.php

388 lines 11.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) die('Access denied.');
4
5 if (!class_exists('WP_Optimize_Utils')) :
6
7 class WP_Optimize_Utils {
8
9 /**
10 * Returns the folder path for log files
11 *
12 * @return string - Folder path for log files with trailing slash
13 */
14 public static function get_log_folder_path() {
15 $upload_base = self::get_base_upload_dir();
16 if (!is_dir($upload_base . 'wpo/logs')) {
17 wp_mkdir_p($upload_base . 'wpo/logs');
18 }
19 // Ensure index.php in log folder to stop directory listing
20 if (!file_exists($upload_base . 'wpo/logs/index.php')) {
21 file_put_contents($upload_base . 'wpo/logs/index.php', "<?php");
22 }
23 return $upload_base . 'wpo/logs/';
24 }
25
26 /**
27 * Generates a log file name based on the given prefix.
28 *
29 * @param string $prefix The prefix to be added to the log file name.
30 * @return string The generated log file name.
31 */
32 public static function get_log_file_name($prefix) {
33 $secret = defined('AUTH_KEY') ? AUTH_KEY : 'WP_Optimize';
34 return $prefix . '-' . substr(md5($secret), 0, 20) . '.log';
35 }
36
37 /**
38 * Returns the file path for the log file.
39 *
40 * @param string $prefix The prefix to be added to the log file name.
41 * @return string The file path for the log file.
42 */
43 public static function get_log_file_path($prefix) {
44 return self::get_log_folder_path() . self::get_log_file_name($prefix);
45 }
46
47 /**
48 * Returns WordPress GMT offset in seconds.
49 *
50 * @return int
51 */
52 public static function get_gmt_offset() {
53 $timezone_string = get_option('timezone_string');
54
55 if (!empty($timezone_string)) {
56 $timezone = new DateTimeZone($timezone_string);
57 $gmt_offset = $timezone->getOffset(new DateTime());
58 } else {
59 $gmt_offset_option = (int) get_option('gmt_offset');
60 $gmt_offset = 3600 * $gmt_offset_option;
61 }
62
63 return $gmt_offset;
64 }
65
66 /**
67 * Returns the folder path for the upload directory with trailing slash
68 *
69 * @return string - Folder path for the upload directory with trailing slash
70 */
71 public static function get_base_upload_dir() {
72 $upload_dir = wp_upload_dir();
73 return trailingslashit($upload_dir['basedir']);
74 }
75
76 /**
77 * Get the file path
78 *
79 * @param string $url
80 * @return string
81 */
82 public static function get_file_path($url) {
83 if (is_multisite()) {
84 if (function_exists('get_main_site_id')) {
85 $site_id = get_main_site_id();
86 } else {
87 $network = get_network();
88 $site_id = $network->site_id;
89 }
90 switch_to_blog($site_id);
91 }
92 $upload_dir = wp_upload_dir();
93 $uploads_url = trailingslashit($upload_dir['baseurl']);
94 $uploads_dir = trailingslashit($upload_dir['basedir']);
95 if (is_multisite()) {
96 restore_current_blog();
97 }
98 $possible_urls = array(
99 WP_CONTENT_URL => WP_CONTENT_DIR,
100 WP_PLUGIN_URL => WP_PLUGIN_DIR,
101 $uploads_url => $uploads_dir,
102 get_template_directory_uri() => get_template_directory(),
103 untrailingslashit(includes_url()) => ABSPATH . WPINC,
104 );
105 $file = '';
106 foreach ($possible_urls as $possible_url => $path) {
107 $pos = strpos($url, $possible_url);
108 if (0 === $pos) {
109 $file = substr_replace($url, $path, $pos, strlen($possible_url));
110 break;
111 }
112 }
113 return $file;
114 }
115
116 /**
117 * Returns folder staticstics - size and file count
118 *
119 * @param string $folder
120 * @return array
121 */
122 public static function get_folder_stats($folder, $files_to_ignore = array()) {
123 clearstatcache();
124
125 $size = 0;
126 $file_count = 0;
127
128 if (is_dir($folder)) {
129 try {
130 $dir = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($folder, FilesystemIterator::SKIP_DOTS));
131
132 foreach ($dir as $file) {
133 if (!empty($files_to_ignore) && is_array($files_to_ignore) && in_array($file->getFilename(), $files_to_ignore)) continue;
134 $size += $file->getSize();
135 $file_count++;
136 }
137 } catch (UnexpectedValueException $e) {
138 error_log($e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Catching exception for debugging purpose
139 }
140 }
141
142 return array(
143 'size' => $size,
144 'file_count' => $file_count,
145 );
146 }
147
148 /**
149 * Fetches the content of a remote file via HTTP request.
150 *
151 * @param string $url The URL of the remote file to retrieve.
152 * @param array $args Request arguments passed to wp_remote_get()
153 * @return string|false The content of the remote file on success, or false on failure.
154 */
155 public static function get_remote_file_content($url, $args = array()) {
156 $response = wp_safe_remote_get($url, $args);
157 if (is_wp_error($response) || 200 !== wp_remote_retrieve_response_code($response)) return false;
158
159 $body = wp_remote_retrieve_body($response);
160 if (empty($body)) return false;
161
162 return $body;
163 }
164
165 /**
166 * Parse tag attributes and return array with them.
167 *
168 * @param string $tag
169 * @return array
170 */
171 public static function parse_attributes($tag) {
172 $attributes = array();
173
174 $_attributes = wp_kses_hair($tag, wp_allowed_protocols());
175
176 if (empty($_attributes)) return $attributes;
177
178 foreach ($_attributes as $key => $value) {
179 $attributes[$key] = $value['value'];
180 }
181
182 return $attributes;
183 }
184
185 /**
186 * Checks whether supplied string is a valid html document or not
187 *
188 * @param string $html - HTML document as string
189 * @return bool
190 */
191 public static function is_valid_html($html) {
192 global $wp_query;
193
194 // is_feed() works only when $wp_query is set, and it raises a warning otherwise.
195 if (isset($wp_query) && is_feed()) return false;
196
197 // To prevent issue with `simple_html_dom` class
198 // Exit if it doesn't look like HTML
199 // https://github.com/rosell-dk/webp-express/issues/228
200 if (!preg_match("#^\\s*<#", $html)) return false;
201
202 if ('' === $html) return false;
203 return true;
204 }
205
206 /**
207 * Include simple html dom script if not available
208 */
209 public static function maybe_include_simple_html_dom() {
210 if (!function_exists('str_get_html')) {
211 require_once WPO_PLUGIN_MAIN_PATH . 'vendor/simplehtmldom/simplehtmldom/simple_html_dom.php';
212 }
213 }
214
215 /**
216 * Returns simplehtmldom\HtmlDocument object
217 *
218 * @param string $html_buffer - HTML document as string
219 * @return simplehtmldom\HtmlDocument | boolean
220 */
221 public static function get_simple_html_dom_object($html_buffer) {
222 self::maybe_include_simple_html_dom();
223 return str_get_html($html_buffer, false, false,
224 get_option('blog_charset'), false, DEFAULT_BR_TEXT,
225 DEFAULT_SPAN_TEXT, false);
226 }
227
228 /**
229 * Unserialize data
230 *
231 * @param string $serialized_data Data to be unserialized, should be one that is already serialized
232 * @param boolean|array $allowed_classes Either an array of class names which should be accepted, false to accept no classes, or true to accept all classes
233 * @param integer $max_depth The maximum depth of structures permitted during unserialization, and is intended to prevent stack overflows
234 * @return mixed Unserialized data can be any of types (integer, float, boolean, string, array or object)
235 */
236 public static function unserialize($serialized_data, $allowed_classes = false, $max_depth = 0) {
237 // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound -- Used in PHP 7.0+
238 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress PHP warning in case of failure
239 return @unserialize(trim($serialized_data), array('allowed_classes' => $allowed_classes, 'max_depth' => $max_depth));
240 }
241
242 /**
243 * Checks whether supplied data is serialized or not, and if so, unserializes it
244 *
245 * @param string $serialized_data Data to be unserialized, should be one that is already serialized
246 * @param boolean|array $allowed_classes Either an array of class names which should be accepted, false to accept no classes, or true to accept all classes
247 * @param integer $max_depth The maximum depth of structures permitted during unserialization, and is intended to prevent stack overflows
248 * @return mixed Unserialized data can be any of types (integer, float, boolean, string, array or object)
249 */
250 public static function maybe_unserialize($serialized_data, $allowed_classes = false, $max_depth = 0) {
251 if (!is_serialized($serialized_data)) return $serialized_data;
252
253 return self::unserialize($serialized_data, $allowed_classes, $max_depth);
254 }
255
256 /**
257 * Get associative array with tag attributes and their values and build tag attribute string.
258 *
259 * @param array $attributes
260 * @return string
261 */
262 public static function build_attributes($attributes) {
263 $_attributes = array();
264
265 if (!empty($attributes)) {
266 foreach ($attributes as $key => $value) {
267 $_attributes[] = $key . '="' . esc_attr($value) . '"';
268 }
269 }
270
271 return join(' ', $_attributes);
272 }
273
274 /**
275 * Get user_agent for desktop or mobile used for different requests via wp_remote_get
276 *
277 * @param string $type
278 *
279 * @return string
280 */
281 public static function get_user_agent($type = 'desktop') {
282 global $wp_version;
283
284 $user_agent = sprintf(
285 'WP-Optimize/%s WordPress/%s (%s; %s)',
286 WPO_VERSION,
287 $wp_version,
288 ('mobile' === $type) ? 'Mobile' : 'Desktop',
289 home_url('/')
290 );
291
292 if ('gfont' === $type) {
293 // Custom UA returns TTF format. We use modern UA to get WOFF2 which is better for web
294 $user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36';
295 }
296
297 return apply_filters('wpo_user_agent', $user_agent, $type);
298 }
299
300 /**
301 * Builds a one-line summary string from debug backtrace.
302 *
303 * Intended for use in error_log() for identifying the caller's context.
304 * Example output: C:Test_Class|F:test_function()|L:100
305 *
306 * @return string
307 */
308 public static function get_backtrace_summary() {
309 $debug_backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace -- Used for debugging purposes
310 $caller = $debug_backtrace[2] ?? array();
311
312 $class = $caller['class'] ?? 'N/A';
313 $function = $caller['function'] ?? 'N/A';
314 $line = $debug_backtrace[1]['line'] ?? 'N/A';
315
316 return sprintf('C:%s|F:%s()|L:%s', $class, $function, $line);
317 }
318
319 /**
320 * Check if the server is using HTTP/1.x
321 *
322 * @return bool
323 */
324 public static function is_request_protocol_http1(): bool {
325 $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_PROTOCOL'])) : '';
326 return stripos($protocol, 'HTTP/1.') !== false;
327 }
328
329 /**
330 * Wrapper for wp_delete_file() to ensure compatibility with WordPress versions prior to 6.1.
331 *
332 * In WordPress versions earlier than 6.1, wp_delete_file() returns void, in that case we check
333 * if the file is actually deleted or not to determine the boolean value to return
334 *
335 * @param string $file
336 * @return bool
337 */
338 public static function wp_delete_file($file) {
339 $wp_delete_file_result = wp_delete_file($file);
340
341 // when wp_delete_file() returns void we check if the file is deleted
342 if (null === $wp_delete_file_result) {
343 $wp_delete_file_result = false === is_file($file);
344 }
345
346 return $wp_delete_file_result;
347 }
348
349 /**
350 * Add UTM parameters to a URL and return the modified URL.
351 *
352 * @param string $url The original URL.
353 * @param array $params Optional UTM parameters.
354 * @param bool $override_url_params if true, it will override existing parameters in the url if matched.
355 *
356 * @return string Modified URL with UTM parameters added.
357 */
358 public static function add_utm_params($url, $params = array(), $override_url_params = false): string {
359 $default_params = array(
360 'utm_source' => 'wpo-plugin',
361 'utm_medium' => 'referral',
362 );
363
364 $utm_params = wp_parse_args($params, $default_params);
365
366 if ($override_url_params) {
367 return esc_url(add_query_arg($utm_params, $url));
368 }
369
370 $parsed = wp_parse_url($url, PHP_URL_QUERY);
371 $original_url_params = array();
372
373 if (!empty($parsed)) {
374 parse_str($parsed, $original_url_params);
375 }
376
377 foreach ($utm_params as $key => $value) {
378 if (isset($original_url_params[$key])) {
379 unset($utm_params[$key]);
380 }
381 }
382
383 return esc_url(add_query_arg($utm_params, $url));
384 }
385 }
386
387 endif;
388