8{%<;6##Ovm'ŽZHţMs+7~ۼkT$1F?Agay9p:.6>Z4\4GE,@BS"F@ `x3Z2ng. t_t{2*<1Î!!v$32*, ~9oA2&#D`_tǑ@j]tQ.vHAHPŐH p8TV> xh,*b?*uA*<{*&CP;H<>(j [rQXk#xP >9%~P=LEzԠ aqm]h7E\K4yAf#  248]"NsF<^_˺?/(Sp6k7 KRCɽoy"2O,9i e[geܔloׯs xD&I `D;߰,"j4{ Uj@5nWN_K=mgf| <1]]31 2 Nه  (8դ-sWL|;D]g>k3&qZݯ&d.bF;R0wvjpa; ^wBkmIR62?=z&]KeTlϕ鈊#1 6u<<6*qb?LQ]ǜD氮"ʬ,7 K0̏ z\#cJ'?*wpw{,dbc\^ ?][>'n)hU9rw> -^K{6ч+(!s9}Ή6u7YLv].9>!jlɴXpnŸ8?@65)8xOIJX,s.M1#8'O7"V:  kGȺ{Pt[WXO fz m(̦q0cB ce7 ѥV 7 gT=dB! ̑-ŖJoYw\PLSSZH3/A кpzBS3=8A ʗ8ϴhpZ v*h`Y`#R#xZbi(س.18xowI{6޺992Z~Eۆap4v$xx+]jG7u#F렟|s댅cF{c5,vִY'/8<^~u!K92<4VqʒKN:׫¡C/-}w UC 24N.ANЂ=*h9sg9ZнZ}Tz>{o ū$xَr0XS +37IU;z-goMe~Z~-NsJ8 h_i)B s&G='HVFs_rMePÖQvWnKv |k[3`JH`sT1Kw0Pj c%03-ܓ( >CnX4yroW @uq9~l(S9<ϟܭ,SpwN#:׺4?@k0w~<"IWQXnBCWF ~>2d죛i'/tzjqHoF7EgG&3{!Ǯ(]BeUȎ݋  .' >pLnWCE*~ia Q{սm-uxE /_Ho[= X=gKUO'꼦^xLjY'YUP##KΌnƏ|M\gYx'#$D]aU\m&}\G*K]QcIhiT4хO{Ї_Arg Z(-3 wI+0V=fDmLr;qU/Л)g*T~Q8Ȱe٪TQ脹0= L,x\ny`֠LkY @@#cv ?9Q(:)+9o;PvDmYZ!}~wʷʗyeaumKM'{ J\8cƸEӼR%_5?*y}R^oV4o_L LV=^, \oNUB_;C㡼Tͩ;V&pUaˆg}SȹJ$Ј(0O,}W?fr2*]YMF\6H~| .n =H 9'I:@De>9cdVJhSN R 0K[oݧH(&FQzҲ}!w[1}#`v5U6ʼ9o=Bp54m%ҊLi]e[6 kf ʮtJ> i*ڀkpiNJbriu;uV(E^VwoûnSSa}mjzɫqH74Of_6=YzdFc`7=  oM;&2k.t*+k~r#<:O~ ^Z4,bj Z>`;ڭ N'N2fɅ4%|J58+߉&VDؼZ`4'O\j9g|¼('@LN,DS[ԉ.Cッ` HUtL'ˈ1#pGu's[1 Hm8Q9xwZ6^p]sF|Ѭh~-߰{>9Y@:9,)  Js!}q;&߆:9*2Xܴ{QivّX6ǍlNX'"y_bD"a αR_#WZEGd)z{#}R)gzk= 9f7ÙwqI2ar)J)RIӫ}9]}QԌ N\:?\Mozd PѴўٍYPUu.>q2fNJBՔ+ռ5[A? ʩl^EhPcR7?նUquƘ`ڌUĢ R~()о:8=q?5W]u7q szps .q"m˚ow埡Ycٗ) 3Uw[k;'ɹ4tpʲS!{hZj`*-p^ыW`4譥8Lp|q |uAI!m{xZ[g-H3{ _gsjMZf9U.ُ-- >)> L]e-b;zCD~zc`/(B>5UGxkF&$Vf֌|E!d~NOBz0uS<ݓ4A 4z|-~ǂq{_Rgc DCVUHنǖ\wN6{Ty~͋޷\]ne[N+"?]><&br]fQ+-w ъoKŵ_l1cs-?8L%ņBw|>_pӽ_Ԩ&!]Fk(G^:Æ+@QYF%f_4]ffIDk&}x$MXx #4ǟH8*<-;X3,TK_R+V@f zZ;7Q;4r~ r%&Qbm@LhVȳ鿩iLb1Ƃ'سMr.1E\ J/I*H0Z?+^.&rOR ^dC .xˋ1EL3)ęQ?hFco,d_Ɔfhk)3&֥jN φe6ʥ#D\Mr{Hn.&lwI':hC]bsa|HWU3Ð/îs}>&9Z,5 "S8.0 kdA͑%'>^K9-Z)Ѫ+gzfRYDyuHDyW5 ^rms{#畜]($p;º/sG̋k'(Tف؇=۬PZ a x7=sJRxY(z,5$:>y )N_߳#g7V km+2+JUn8(⑆gP]7~=w&섉GpALl o8a]Cȏ# k3"I)ul6_ZoG VP2㘐Y$iP&]ߓD4d{~%9[jjS0 SGh nrfOň,QJH+-X(5{+3gT'1T"GdLx8l.Q(I^.meF:'p )mVB.mf7B~*R7j'%? RѺ7F=MLe*wnR?,$M&:V a!O=uZ+ѵ^iLs#< I=-3eڟ_ƴxJ8%_ ;VgsÏXvmM(ZW~?~ zO~e1Cr>115 ;zCr n"^)%#F1Ww9}ꚬ`r|A3C<'/%-$/*6ī-kVd.4.tS{};0{n55Yq цeӞ)Ja{ۭyg=p}e*ئ5`i]k(BÑH\ lSD8o[ ֶelq@cK=O訳<; L"bPS/ 0kZ;A 'J}пRu/+[XJSo>鴬[B&͑)kմ">ɜݖ(!ǖ Bd0A )ON&by"}D.1;΍YVV>S ,P&:`~ ޼7j 0kUPl5!s.W6)1kDq`Kle-r,0e;lR5S֥gCy=s<bAjx&G2Ĕz=EI?3Bzߌ/>:!)"oqQ.\1(U 957S&{1&M6'ZCT˜]2!˵fl+"сayVHuk^TcAV 7OISWK*'^SdK9p!,rSu (tŕ>]:;3Ur߄m6I׎Yvmf#ܓ,De(A lr iEJ:q§yuTDZ̺uƬѦaH5 ÊdbQ/\YíHxAA^P tB^P6M[AZ+tk\]-qI d娗xCgih 6PYQts爫bθM;9W'5'7PO?`17Q\JL%c.҂5Շ(Ël4fP tcbisttcYOIj7xxuGKṚ'cPĥ"<kCY06?ƌJ͆ ;q>E& 2~ }5Z3"QkcN /Z %.Z&H%l/+јard}DЄ8pd}Dn'u&PUD-zpҽ5&ɒdYxokЕ[9VH ?/0GJ5J: Q DO`t#'(ֹIh^.:u.h<G v ^4BnVdXQֺW/7gSq]/Y 6_K>VN&kb>4Q>й#Pv:hVdSh3RC)SB)3x~T8Qv[}CpP`ۛ2ĉc[tmtTi 뒖/nDŽO!x8j ~*:W{.gT8_m#nuv(p[cO]>\#"1,_"A[?kj][IF;+>s 'F}nTl'jjWn6n mVKz3Gl:p W<YwKĭϊ~Md''F51Ke?c*c;R!=.>,$ &'P?SDS|7/i3aj2jPkrRmgR ,5ya>VW8qZlir&73o=Qʴk\_\v9JiVAi&3 r40T- Cr(!ra ly%MimTu0T>qkq}5p ?JN_' fx#%SjpY7$!?rW_68ªzv/+7Ȼ2t?j>o~H]U=o{db`-rƥMBM7 6}~vwm 8)%]":A 0rOiQZ\9jB^EL|T}.bodeyO[VW粩"J.B@n\Դ/'VU?ـaWJxIUr=Eo0pA8]|:7_Ɛ`l n卪51',IJ)q:>>ZiQ\i c|Yc*ylFm98LuowO…uZG{ݯ8=ò群;25I$z)\A4魽dI3 $qx^3w#Xct>[|B<+b,Q0z@K'݈I] "2Kc pqffFglk;͢! G/5/UŰ|֙&(& q". ޛ\@ttۊ_ 0|Nn/Ƽ%ڿ0û [YQῚ3N\0׬L8,D޶ ZCp?~ Pu88|nѹrLe?QV8 A?@|x㺚4*1 $value) { $key = strtolower($key); if ('HTTP_' == substr($key, 0, 5)) { $headers[str_replace(' ', '-', ucwords(str_replace('_', ' ', substr($key, 5))))] = $value; } elseif ('content_type' == $key) { $headers["Content-Type"] = $value; } elseif ('content_length' == $key) { $headers["Content-Length"] = $value; } } } return $headers; } endif; /** * Check if requested Accept-Encoding headers has gzip value. * * @return bool */ if (!function_exists('wpo_cache_gzip_accepted')) : function wpo_cache_gzip_accepted() { $headers = wpo_get_http_headers(); if (isset($headers['Accept-Encoding']) && preg_match('/gzip/i', $headers['Accept-Encoding'])) return true; return false; } endif; /** * Check if we can output gzip content in current answer, i.e. check Accept-Encoding headers has gzip value * and function ob_gzhandler is available. * * @return bool */ if (!function_exists('wpo_cache_can_output_gzip_content')) : function wpo_cache_can_output_gzip_content() { return wpo_cache_gzip_accepted() && function_exists('ob_gzhandler'); } endif; /** * Check if header with certain name exists in already prepared headers and has value comparable with $header_value. * * @param string $header_name header name * @param string $header_value header value as regexp. * * @return bool */ if (!function_exists('wpo_cache_is_in_response_headers_list')) : function wpo_cache_is_in_response_headers_list($header_name, $header_value) { $headers_list = headers_list(); if (!empty($headers_list)) { $header_name = strtolower($header_name); foreach ($headers_list as $value) { $value = explode(':', $value); if (strtolower($value[0]) == $header_name) { if (preg_match('/'.$header_value.'/', $value[1])) { return true; } else { return false; } } } } return false; } endif; /** * Check if mobile cache is enabled and current request is from moblile device. * * @return bool */ if (!function_exists('wpo_cache_mobile_caching_enabled')) : function wpo_cache_mobile_caching_enabled() { if (!empty($GLOBALS['wpo_cache_config']['enable_mobile_caching'])) return true; return false; } endif; /** * Check if webp images enabled * * @return bool */ if (!function_exists('wpo_webp_images_enabled')) : function wpo_webp_images_enabled() { if (!empty($GLOBALS['wpo_cache_config']['use_webp_images'])) return true; return false; } endif; /** * Check whether webp images using alter html method or not * * @return bool */ if (!function_exists('wpo_is_using_alter_html')) : function wpo_is_using_alter_html() { return (isset($_SERVER['HTTP_ACCEPT']) && false !== strpos($_SERVER['HTTP_ACCEPT'], 'image/webp')); } endif; /** * Check whether webp images are served using redirect * * @return bool */ if (!function_exists('wpo_is_using_webp_images_redirection')) : function wpo_is_using_webp_images_redirection() { if (empty($GLOBALS['wpo_cache_config']['uploads'])) return false; $uploads_dir = $GLOBALS['wpo_cache_config']['uploads']; $htaccess_file = $uploads_dir . '/.htaccess'; if (!file_exists($htaccess_file)) return false; $htaccess_content = file_get_contents($htaccess_file); $comment_sections = array('Register webp mime type', 'WP-Optimize WebP Rules'); if (function_exists('str_contains')) { return str_contains($htaccess_content, $comment_sections[0]) && str_contains($htaccess_content, $comment_sections[1]); } else { return strpos($htaccess_content, $comment_sections[0]) && strpos($htaccess_content, $comment_sections[1]); } } endif; /** * Serves the cache and exits */ if (!function_exists('wpo_serve_cache')) : function wpo_serve_cache() { $file_name = wpo_cache_filename(); $file_name_rss_xml = wpo_cache_filename('.rss-xml'); $send_as_feed = false; $path_dir = WPO_CACHE_FILES_DIR . '/' . wpo_get_url_path() . '/'; $path = $path_dir . $file_name; if (wpo_feeds_caching_enabled()) { // check for .xml cache file if .html cache file doesn't exist if (!file_exists($path_dir . $file_name) && file_exists($path_dir . $file_name_rss_xml)) { $path = $path_dir . $file_name_rss_xml; $send_as_feed = true; } } $use_gzip = false; // if we can use gzip and gzipped file exist in cache we use it. // if headers already sent we don't use gzipped file content. if (!headers_sent() && wpo_cache_gzip_accepted() && file_exists($path . '.gz')) { $path .= '.gz'; $use_gzip = true; } $modified_time = file_exists($path) ? (int) filemtime($path) : time(); // Cache has expired, purge and exit. if (!empty($GLOBALS['wpo_cache_config']['page_cache_length'])) { if (time() > ($GLOBALS['wpo_cache_config']['page_cache_length'] + $modified_time)) { wpo_delete_files($path); return; } } // disable zlib output compression to avoid double content compression. if ($use_gzip) { ini_set('zlib.output_compression', 'Off'); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_ini_set } $gzip_header_already_sent = wpo_cache_is_in_response_headers_list('Content-Encoding', 'gzip'); header('Cache-Control: no-cache'); // Check back later if (!empty($modified_time) && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $modified_time) { if ($use_gzip && !$gzip_header_already_sent) { header('Content-Encoding: gzip'); } if ($send_as_feed) { header('Content-type: application/rss+xml'); } header('WPO-Cache-Status: cached'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $modified_time) . ' GMT'); header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304); exit; } if (file_exists($path) && is_readable($path)) { if (wpo_is_canonical_redirection_needed()) return; if ($use_gzip && !$gzip_header_already_sent) { header('Content-Encoding: gzip'); } // send correct headers for xml and txt files $filename = basename(dirname($path)); if (preg_match('/\.xml$/i', $filename)) { header('Content-type: text/xml'); } if (preg_match('/\.txt$/i', $filename)) { header('Content-type: text/plain'); } if ($send_as_feed) { header('Content-type: application/rss+xml'); } header('WPO-Cache-Status: cached'); if (!empty($modified_time)) { header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $modified_time) . ' GMT'); } readfile($path); exit; } } endif; /** * Checks and does redirection, if needed * * @return bool */ if (!function_exists('wpo_is_canonical_redirection_needed')) : function wpo_is_canonical_redirection_needed() { $permalink_structure = isset($GLOBALS['wpo_cache_config']['permalink_structure']) ? $GLOBALS['wpo_cache_config']['permalink_structure'] : ''; $site_url = $GLOBALS['wpo_cache_config']['site_url']; $schema = isset($_SERVER['HTTPS']) && 'on' === $_SERVER['HTTPS'] ? "https" : "http"; $url_part = "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $requested_url = $schema . $url_part; $url_parts = parse_url($requested_url); $extension = pathinfo($url_parts['path'], PATHINFO_EXTENSION); if (!empty($permalink_structure) && $requested_url != $site_url) { if ('/' == substr($permalink_structure, -1) && empty($extension) && empty($url_parts['query']) && empty($url_parts['fragment'])) { $url = preg_replace('/(.+?)([\/]*)(\[\?\#][^\/]+|$)/', '$1/$3', $_SERVER['REQUEST_URI']); if (0 !== strcmp($_SERVER['REQUEST_URI'], $url)) return true; } else { $url = rtrim($_SERVER['REQUEST_URI'], '/'); if (0 !== strcmp($_SERVER['REQUEST_URI'], $url)) return true; } } return false; } endif; /** * Clears the cache */ if (!function_exists('wpo_cache_flush')) : function wpo_cache_flush() { if (defined('WPO_CACHE_FILES_DIR') && '' != WPO_CACHE_FILES_DIR) wpo_delete_files(WPO_CACHE_FILES_DIR); if (function_exists('wp_cache_flush')) { wp_cache_flush(); } do_action('wpo_cache_flush'); } endif; /** * Get URL path for caching * * @since 1.0 * @return string */ if (!function_exists('wpo_get_url_path')) : function wpo_get_url_path($url = '') { $url = '' == $url ? wpo_current_url() : $url; $url_parts = parse_url($url); if (isset($url_parts['path']) && false !== stripos($url_parts['path'], '/index.php')) { $url_parts['path'] = preg_replace('/(.*?)index\.php(\/.+)/i', '$1index-php$2', $url_parts['path']); } if (!isset($url_parts['host'])) $url_parts['host'] = ''; if (!isset($url_parts['path'])) $url_parts['path'] = ''; return $url_parts['host'].$url_parts['path']; } endif; /** * Get requested url. * * @return string */ if (!function_exists('wpo_current_url')) : function wpo_current_url() { // Note: We use `static $url` to save the first value we retrieve, as some plugins change $_SERVER later on in the process (e.g. Weglot). // Otherwise this function would return a different URL at the begining and end of the cache process. static $url = ''; if ('' != $url) return $url; $http_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''; $url = rtrim('http' . ((isset($_SERVER['HTTPS']) && ('on' == $_SERVER['HTTPS'] || 1 == $_SERVER['HTTPS']) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO']) ? 's' : '' ) . '://' . $http_host.$_SERVER['REQUEST_URI'], '/'); return $url; } endif; /** * Return list of conditional tag exceptions. * * @return array */ if (!function_exists('wpo_get_conditional_tags_exceptions')) : function wpo_get_conditional_tags_exceptions() { static $exceptions = null; if (null !== $exceptions) return $exceptions; if (!empty($GLOBALS['wpo_cache_config'])) { if (empty($GLOBALS['wpo_cache_config']['cache_exception_conditional_tags'])) { $exceptions = array(); } else { $exceptions = $GLOBALS['wpo_cache_config']['cache_exception_conditional_tags']; } } elseif (class_exists('WPO_Page_Cache')) { $config = WPO_Page_Cache::instance()->config->get(); if (is_array($config) && array_key_exists('cache_exception_conditional_tags', $config)) { $exceptions = $config['cache_exception_conditional_tags']; } else { $exceptions = array(); } $exceptions = is_array($exceptions) ? $exceptions : preg_split('#(\n|\r|\r\n)#', $exceptions); $exceptions = array_filter($exceptions, 'trim'); } else { $exceptions = array(); } return $exceptions; } endif; /** * Return list of url exceptions. * * @return array */ if (!function_exists('wpo_get_url_exceptions')) : function wpo_get_url_exceptions() { static $exceptions = null; if (null !== $exceptions) return $exceptions; // if called from file-based-page-cache.php when WP loading // and cache settings exists then use it otherwise get settings from database. if (!empty($GLOBALS['wpo_cache_config'])) { if (empty($GLOBALS['wpo_cache_config']['cache_exception_urls'])) { $exceptions = array(); } else { $exceptions = is_array($GLOBALS['wpo_cache_config']['cache_exception_urls']) ? $GLOBALS['wpo_cache_config']['cache_exception_urls'] : preg_split('#(\n|\r)#', $GLOBALS['wpo_cache_config']['cache_exception_urls']); } } elseif (class_exists('WPO_Page_Cache')) { $config = WPO_Page_Cache::instance()->config->get(); if (is_array($config) && array_key_exists('cache_exception_urls', $config)) { $exceptions = $config['cache_exception_urls']; } else { $exceptions = array(); } $exceptions = is_array($exceptions) ? $exceptions : preg_split('#(\n|\r)#', $exceptions); $exceptions = array_filter($exceptions, 'trim'); } else { $exceptions = array(); } return apply_filters('wpo_get_url_exceptions', $exceptions); } endif; /** * Return true of exception url matches current url * * @param string $exception Exceptions to check URL against. * @param bool $regex Whether to check with regex or not. * @return bool true if matched, false otherwise */ if (!function_exists('wpo_current_url_exception_match')) : function wpo_current_url_exception_match($exception) { return wpo_url_exception_match(wpo_current_url(), $exception); } endif; /** * Check if url in conditional tags exceptions list. * * @return string */ if (!function_exists('wpo_url_in_conditional_tags_exceptions')) : function wpo_url_in_conditional_tags_exceptions() { $exceptions = wpo_get_conditional_tags_exceptions(); $restricted = ''; $allowed_functions = array('is_single', 'is_page', 'is_front_page', 'is_home', 'is_archive', 'is_tag', 'is_category', 'is_feed', 'is_search', 'is_author', 'is_woocommerce', 'is_shop', 'is_product', 'is_account_page', 'is_product_category', 'is_product_tag', 'is_wc_endpoint_url', 'is_bbpress', 'bbp_is_forum_archive', 'bbp_is_topic_archive', 'bbp_is_topic_tag', 'bbp_is_single_forum', 'bbp_is_single_topic', 'bbp_is_single_view', 'bbp_is_single_user', 'bbp_is_user_home', 'bbp_is_search'); //Filter for add more conditional tags to whitelist in the exceptions list. $allowed_functions = apply_filters('wpo_allowed_conditional_tags_exceptions', $allowed_functions); if (!empty($exceptions)) { foreach ($exceptions as $exception) { if (false !== strpos($exception, 'is_')) { $exception_function = $exception; if ('()' == substr($exception, -2)) { $exception_function = substr($exception, 0, -2); } if (in_array($exception_function, $allowed_functions) && function_exists($exception_function) && call_user_func($exception_function)) { $restricted = sprintf(__('In the settings, caching is disabled for %s', 'wp-optimize'), $exception_function); } } } } return $restricted; } endif; /** * Check if url in exceptions list. * * @param string $url * * @return bool */ if (!function_exists('wpo_url_in_exceptions')) : function wpo_url_in_exceptions($url) { $exceptions = wpo_get_url_exceptions(); if (!empty($exceptions)) { foreach ($exceptions as $exception) { // don't check / - front page using regexp, we handle it in wpo_restricted_cache_page_type() if ('/' == $exception) continue; if (wpo_url_exception_match($url, $exception)) { // Exception match. return true; } } } return false; } endif; /** * Check if url string match with exception. * * @param string $url - complete url string i.e. http(s):://domain/path * @param string $exception - complete url or absolute path, can consist (.*) wildcards * * @return bool */ if (!function_exists('wpo_url_exception_match')) : function wpo_url_exception_match($url, $exception) { if (preg_match('#^[\s]*$#', $exception)) { return false; } $exception = str_replace('*', '.*', $exception); $exception = trim($exception); // used to test websites placed in subdirectories. $sub_dir = ''; // if exception defined from root i.e. /page1 then remove domain part in url. if (preg_match('/^\//', $exception)) { // get site sub directory. $sub_dir = preg_replace('#^(http|https):\/\/.*\/#Ui', '', wpo_site_url()); // add prefix slash and remove slash. $sub_dir = ('' == $sub_dir) ? '' : '/' . rtrim($sub_dir, '/'); // get relative path $url = preg_replace('#^(http|https):\/\/.*\/#Ui', '/', $url); } $url = rtrim($url, '/') . '/'; $exception = rtrim($exception, '/'); // if we have no wildcat in the end of exception then add slash. if (!preg_match('#\(\.\*\)$#', $exception)) $exception .= '/'; $exception = preg_quote($exception); // fix - unescape possible escaped mask .* $exception = str_replace('\\.\\*', '.*', $exception); return preg_match('#^'.$exception.'$#i', $url) || preg_match('#^'.$sub_dir.$exception.'$#i', $url); } endif; /** * Checks if its a mobile device * * @see https://developer.wordpress.org/reference/functions/wp_is_mobile/ */ if (!function_exists('wpo_is_mobile')) : function wpo_is_mobile() { if (empty($_SERVER['HTTP_USER_AGENT'])) { $is_mobile = false; // many mobile devices (all iPhone, iPad, etc.) } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'Android') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'Silk/') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'Kindle') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'BlackBerry') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mini') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mobi') !== false ) { $is_mobile = true; } else { $is_mobile = false; } return $is_mobile; } endif; /** * Check if current browser agent is not disabled in options. * * @return bool */ if (!function_exists('wpo_is_accepted_user_agent')) : function wpo_is_accepted_user_agent($user_agent) { $exceptions = is_array($GLOBALS['wpo_cache_config']['cache_exception_browser_agents']) ? $GLOBALS['wpo_cache_config']['cache_exception_browser_agents'] : preg_split('#(\n|\r)#', $GLOBALS['wpo_cache_config']['cache_exception_browser_agents']); if (!empty($exceptions)) { foreach ($exceptions as $exception) { if ('' == trim($exception)) continue; if (preg_match('#'.$exception.'#i', $user_agent)) return false; } } return true; } endif; /** * Delete function that deals with directories recursively * * @param string $src Path of the folder * @param boolean $recursive If $src is a folder, recursively delete the inner folders. If set to false, only the files will be deleted. * * @return bool */ if (!function_exists('wpo_delete_files')) : function wpo_delete_files($src, $recursive = true) { if (!file_exists($src) || '' == $src || '/' == $src) { return true; } if (is_file($src)) { return unlink($src); } $success = true; $has_dir = false; if ($recursive) { // N.B. If opendir() fails, then a false positive (i.e. true) will be returned if (false !== ($dir = opendir($src))) { $file = readdir($dir); while (false !== $file) { if ('.' == $file || '..' == $file) { $file = readdir($dir); continue; } $full_path = $src . '/' . $file; if (is_dir($full_path)) { if (!wpo_delete_files($full_path)) { $success = false; } } else { if (!@unlink($full_path)) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppressed the first time in case it was already removed by a contemporary process (avoid unwanted logging for harmless races) if (file_exists($full_path) && !unlink($full_path)) { $success = false; } } } $file = readdir($dir); } closedir($dir); } } else { // Not recursive, so we only delete the files // scan directories recursively. $handle = opendir($src); if (false === $handle) return false; $file = readdir($handle); while (false !== $file) { if ('.' != $file && '..' != $file) { if (is_dir($src . '/' . $file)) { $has_dir = true; } elseif (!unlink($src . '/' . $file)) { $success = false; } } $file = readdir($handle); } } if ($success && !$has_dir) { // Success of this operation is not recorded; we only ultimately care about emptying, not removing entirely (empty folders in our context are harmless) rmdir($src); } // delete cached information about cache size. WP_Optimize()->get_page_cache()->delete_cache_size_information(); return $success; } endif; if (!function_exists('wpo_is_empty_dir')) : /** * Check if selected directory is empty or has only index.php which we added for security reasons. * * @param string $dir * * @return bool */ function wpo_is_empty_dir($dir) { if (!file_exists($dir) || !is_dir($dir)) return false; $handle = opendir($dir); if (false === $handle) return false; $is_empty = true; $file = readdir($handle); while (false !== $file) { if ('.' != $file && '..' != $file && 'index.php' != $file) { $is_empty = false; break; } $file = readdir($handle); } closedir($handle); return $is_empty; } endif; /** * Either store for later output, or output now. Only the most-recent call will be effective. * * @param String|Null $output - if not null, then the string to use when called by the shutdown action. */ if (!function_exists('wpo_cache_add_footer_output')) : function wpo_cache_add_footer_output($output = null) { static $buffered = null; if (function_exists('current_filter') && 'shutdown' == current_filter()) { // Only add the line if it was a page, not something else (e.g. REST response) if (function_exists('did_action') && did_action('wp_footer')) { echo "\n\n"; } elseif (defined('WPO_CACHE_DEBUG') && WPO_CACHE_DEBUG) { error_log('[CACHE DEBUG] '.wpo_current_url() . ' - ' . $buffered); } } else { if (null == $buffered && function_exists('add_action')) add_action('shutdown', 'wpo_cache_add_footer_output', 11); $buffered = $output; } } endif; /** * Remove variable names that shouldn't influence cache. * * @param array $variables List of variable names. * * @return array */ if (!function_exists('wpo_cache_maybe_ignore_query_variables')) : function wpo_cache_maybe_ignore_query_variables($variables) { /** * Filters the current $_GET variables that will be used when caching or excluding from cache. * Currently: * - 'wpo_cache_debug' (Shows the reason for not being cached even when WP_DEBUG isn't set) * - 'doing_wp_cron' (alternative cron) * - 'aiosp_sitemap_path', 'aiosp_sitemap_page' (All in one SEO sitemap) * - 'xml_sitemap', 'seopress_sitemap', 'seopress_news', 'seopress_video', 'seopress_cpt', 'seopress_paged' (SEOPress sitemap) * - 'sitemap', 'sitemap_n' (YOAST SEO sitemap) */ $exclude_variables = array( 'wpo_cache_debug', // Shows the reason for not being cached even when WP_DEBUG isn't set 'doing_wp_cron', // alternative cron 'aiosp_sitemap_path', // All in one SEO sitemap 'aiosp_sitemap_page', 'xml_sitemap', // SEOPress sitemap 'seopress_sitemap', 'seopress_news', 'seopress_video', 'seopress_cpt', 'seopress_paged', 'sitemap', // YOAST SEO sitemap 'sitemap_n', ); $exclude_variables = function_exists('apply_filters') ? apply_filters('wpo_cache_ignore_query_variables', $exclude_variables) : $exclude_variables; if (empty($exclude_variables)) return $variables; foreach ($exclude_variables as $variable) { $exclude = array_search($variable, $variables); if (false !== $exclude) { array_splice($variables, $exclude, 1); } } return $variables; } endif; /** * Get cache config * * @param string $key - The config item * @param mixed $default - The default value * * @return mixed */ if (!function_exists('wpo_cache_config_get')) : function wpo_cache_config_get($key, $default = false) { $config = $GLOBALS['wpo_cache_config']; if (!$config) return false; if (isset($config[$key])) { return $config[$key]; } else { return $default; } } endif; if (!function_exists('wpo_disable_cache_directories_viewing')) : function wpo_disable_cache_directories_viewing() { global $is_apache, $is_IIS, $is_iis7; if (!is_dir(WPO_CACHE_FILES_DIR)) return; // Create .htaccess file for apache server. if ($is_apache) { $htaccess_filename = WPO_CACHE_FILES_DIR . '/.htaccess'; // CS does not like heredoc // phpcs:disable $htaccess_content = << Order allow,deny Deny from all EOF; // phpcs:enable if (!is_file($htaccess_filename)) @file_put_contents($htaccess_filename, $htaccess_content); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged } // Create web.config file for IIS servers. if ($is_IIS || $is_iis7) { $webconfig_filename = WPO_CACHE_FILES_DIR . '/web.config'; $webconfig_content = "\n\n\n\n\n\n\n"; if (!is_file($webconfig_filename)) @file_put_contents($webconfig_filename, $webconfig_content); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged } // Create empty index.php file for all servers. if (!is_file(WPO_CACHE_FILES_DIR . '/index.php')) @file_put_contents(WPO_CACHE_FILES_DIR . '/index.php', '');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged } endif; /** * Add the headers indicating why the page is not cached or served from cache * * @param string $message - The headers * * @return void */ if (!function_exists('wpo_cache_add_nocache_http_header')) : function wpo_cache_add_nocache_http_header($message = '') { static $buffered_message = null; if (function_exists('current_filter') && 'send_headers' === current_filter() && $buffered_message && !headers_sent()) { header('WPO-Cache-Status: not cached'); header('WPO-Cache-Message: '. trim(str_replace(array("\r", "\n", ':'), ' ', strip_tags($buffered_message)))); } else { if (!$buffered_message && function_exists('add_action')) add_action('send_headers', 'wpo_cache_add_nocache_http_header', 11); $buffered_message = $message; } } endif; /** * Check if feeds caching enabled * * @return bool */ if (!function_exists('wpo_feeds_caching_enabled')) : function wpo_feeds_caching_enabled() { return apply_filters('wpo_feeds_caching_enabled', true); } endif; if (!function_exists('wpo_debug_backtrace_summary')) { function wpo_debug_backtrace_summary($ignore_class = null, $skip_frames = 0, $pretty = true) { static $truncate_paths; $trace = debug_backtrace(false); $caller = array(); $check_class = !is_null($ignore_class); $skip_frames++; // Skip this function. if (!isset($truncate_paths)) { $truncate_paths = array( wpo_normalize_path(WP_CONTENT_DIR), wpo_normalize_path(ABSPATH), ); } foreach ($trace as $call) { if ($skip_frames > 0) { $skip_frames--; } elseif (isset($call['class'])) { if ($check_class && $ignore_class == $call['class']) { continue; // Filter out calls. } $caller[] = "{$call['class']}{$call['type']}{$call['function']}"; } else { if (in_array($call['function'], array('do_action', 'apply_filters', 'do_action_ref_array', 'apply_filters_ref_array'), true)) { $caller[] = "{$call['function']}('{$call['args'][0]}')"; } elseif (in_array($call['function'], array('include', 'include_once', 'require', 'require_once'), true)) { $filename = isset($call['args'][0]) ? $call['args'][0] : ''; $caller[] = $call['function'] . "('" . str_replace($truncate_paths, '', wpo_normalize_path($filename)) . "')"; } else { $caller[] = $call['function']; } } } if ($pretty) { return implode(', ', array_reverse($caller)); } else { return $caller; } } } if (!function_exists('wpo_normalize_path')) { function wpo_normalize_path($path) { // Standardise all paths to use '/'. $path = str_replace('\\', '/', $path); // Replace multiple slashes down to a singular, allowing for network shares having two slashes. $path = preg_replace('|(?<=.)/+|', '/', $path); // Windows paths should uppercase the drive letter. if (':' === substr($path, 1, 1)) { $path = ucfirst($path); } return $path; } }