: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
defined('ABSPATH') || exit;
if (!class_exists('TFCache',false)) {
if (!class_exists('Themify_Filesystem',false) && defined('TF_CACHE_FW')) {
require_once dirname(TF_CACHE_FW) . '/class-themify-filesystem.php';
* Class to work with post cache
const SEP = DIRECTORY_SEPARATOR;
private static $cache_dir = null;
public static $stopCache = false;
private static $error = false;
* @param integer $post_id
public static function start_cache($tag, $post_id = false, array $args = array(), $time = false) {//backward compatibility for addons
public static function end_cache() {//backward compatibility for addons
* remove cache after some updates
public static function remove_cache($item_id = 'blog', $type = false, $blog_id = false) {
if (isset($queue['all'])) {
if ($item_id === 'all') {
$dir = self::get_cache_main_dir();
if (!Themify_Filesystem::is_dir($dir)) {
return Themify_Filesystem::delete($dir);
$cache_dir = self::get_cache_blog_dir($blog_id);
if (!isset($queue['blog']) && Themify_Filesystem::is_dir($cache_dir)) {
if ($item_id === 'blog') {
return Themify_Filesystem::delete($cache_dir);
$the_post = wp_is_post_revision($item_id);
$post = get_post($item_id);
$type = $post->post_type;
return self::remove_cache();
if (!isset($queue[$k])) {
$find = array(' post-' . $item_id);
$item_id = (int) $item_id;
$find[] = get_post_type($item_id) === 'page' ? ' page-id-' . $item_id : ' postid-' . $item_id; //if there is any html associated with updated post
if ($type === 'comment' || $type === 'term' || $type === 'category') {
$find[] = $type . '-' . $item_id;
if ($type !== 'comment') {
$find[] = $type === 'category' ? get_category_link($item_id) : get_term_link($item_id);
$temp = get_term($item_id);
$find[] = 'term-' . $temp->slug;
if (!self::clear_recursive($cache_dir, $find)) {
return self::remove_cache();
private static function clear_recursive($cache_dir, array $find) {
$dirHandle = opendir($cache_dir);
while ($f = readdir($dirHandle)) {
if ($f !== '.' && $f !== '..') {
$item = rtrim($cache_dir, self::SEP) . self::SEP . $f;
if (Themify_Filesystem::is_dir($item)) {
self::clear_recursive($item, $find);
} elseif (strpos($item, '.html', 5) !== false && strpos($item, '.html.gz', 5) === false && Themify_Filesystem::is_file($item)) {
$content = file_get_contents($item, FALSE, NULL, 2000);
if (strpos($content, $v, 10) !== false) {
Themify_Filesystem::delete($item, 'f');
Themify_Filesystem::delete($item . '.gz', 'f');
* init hooks to update cache
public static function hooks() {
add_action('save_post', array(__CLASS__, 'save'), 100, 3);
add_action('deleted_post', array(__CLASS__, 'save'), 100, 1);
add_action('comment_post', array(__CLASS__, 'comment_update'), 100, 2);
add_action('deleted_comment', array(__CLASS__, 'comment_update'), 100, 2);
add_action('wp_update_nav_menu', array(__CLASS__, 'menu_update'), 100);
add_action('wp_update_nav_menu_item', array(__CLASS__, 'menu_update'), 100);
add_action('activated_plugin', array(__CLASS__, 'plugin_active_deactive'), 100, 2);
add_action('deactivated_plugin', array(__CLASS__, 'plugin_active_deactive'), 100, 2);
add_action('admin_footer', array(__CLASS__, 'admin_check'));
add_action('wp_ajax_themify_write_config', array(__CLASS__, 'ajax_write_wp_cache'));
add_action('customize_save_after', array(__CLASS__, 'customizer'));
add_action('switch_theme', array(__CLASS__, 'disable_cache'), 5);
add_action('edit_term', array(__CLASS__, 'edit_term'), 100, 3);
add_action('delete_term_taxonomy', array(__CLASS__, 'edit_term'), 100, 1);
add_action('check_ajax_referer', array(__CLASS__, 'widget_update'), 100, 2); //for widgets order,there is no hook
$metas = array('post', 'comment', 'term', 'user');
if ($m !== 'term' && $m !== 'user') {
add_action('added_' . $m . '_meta', array(__CLASS__, 'meta_update'), 100, 4);
add_action('updated_' . $m . '_meta', array(__CLASS__, 'meta_update'), 100, 4);
add_action('deleted_' . $m . '_meta', array(__CLASS__, 'meta_update'), 100, 4);
if (is_user_logged_in()) {
add_action('admin_bar_menu', array(__CLASS__, 'cache_menu'), 100);
if (isset($_GET['tf-cache']) && ($_GET['tf-cache'] === '2' || $_GET['tf-cache'] === '4')) {
add_action('init', array(__CLASS__, 'check_clear'), 1);
add_action('upgrader_process_complete', array(__CLASS__, 'themify_updated'), 10, 2);
public static function comment_update($comment_ID, $comment_approved) {
$comment = get_comment($comment_ID);
self::remove_cache($comment->comment_post_ID, 'comment');
* plugin activatiion/deactivation
public static function plugin_active_deactive($plugin, $network_wide) {
$type = $network_wide ? 'all' : 'blog';
self::remove_cache($type);
public static function menu_update($_menu_id) {
themify_clear_menu_cache();
remove_action('wp_update_nav_menu', array(__CLASS__, 'menu_update'), 100);
remove_action('wp_update_nav_menu_item', array(__CLASS__, 'menu_update'), 100);
public static function customizer($manager) {
$post_id = $manager->changeset_post_id();
self::remove_cache($post_id);
public static function edit_term($term, $tt_id = null, $taxonomy = null) {
$taxonomy = $temp->taxonomy;
$type = $taxonomy === 'category' ? 'category' : 'term';
self::remove_cache($term, $type);
public static function meta_update($meta_id, $post_id, $meta_key, $meta_value) {
$actions = explode('_', current_action());
self::remove_cache($post_id, $actions[1]);
public static function widget_update($action, $result) {
if ($result !== false && $action === 'save-sidebar-widgets') {
public static function save($post_id, $post = false, $update = true) {
if ($update || current_action() === 'deleted_post') {
self::remove_cache($post_id);
} elseif (!is_object($post) || $post->post_status !== 'auto-draft') {
public static function get_current_url():string {
if (empty($_SERVER['REQUEST_URI']) || empty($_SERVER['HTTP_HOST'])) {
$protocol = is_ssl() ? 'https://' : 'http://';
return $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
* will be called in advanced-cache.php before wp full core load,a lot of functions from wp api and FW functions are't available in in this function be carefull!
public static function run() {
if (self::$stopCache === true) {
if (!function_exists('wp_get_nocache_headers')) {
add_action('init', array(__CLASS__, 'run'), 0);
self::$cache_dir = self::get_cache_main_dir();
if (Themify_Filesystem::mkdir(self::$cache_dir)) {
$isMulti = is_multisite();
if ($isMulti !== false) {
self::$cache_dir = self::get_cache_blog_dir();
if ($isMulti === false || Themify_Filesystem::mkdir(self::$cache_dir, true)) {
if (defined('TF_CACHE_RULES') && TF_CACHE_RULES) {
$ignore = explode('|TF|', TF_CACHE_RULES);
if (!empty($ignore) && !empty($_SERVER['HTTP_HOST'])) {
$request = $_SERVER['REQUEST_URI'];
$server = is_ssl() ? 'https://' : 'http://';
$server .= $_SERVER['HTTP_HOST'];
foreach ($ignore as $r) {
$r = str_replace($server, '', $r);
if (preg_match($p, $request)) {
$request = $server = $ignore = null;
self::$cache_dir = self::get_current_cache('', true).'.html';
if (Themify_Filesystem::is_file(self::$cache_dir)) {
$ftime = filemtime(self::$cache_dir);
$expire = defined('TF_CACHE_TIME') && TF_CACHE_TIME ? (TF_CACHE_TIME * 60) : WEEK_IN_SECONDS;
$liveTime = $expire + $ftime;
if ($liveTime > time()) {
$headers = apply_filters('wp_headers', wp_get_nocache_headers(), $wp);
if (!isset($headers['Cache-Control'])) {
$headers['Content-Type'] = 'no-cache, must-revalidate, max-age=0';
if (!isset($headers['Content-Type'])) {
$headers['Content-Type'] = 'text/html;charset=UTF-8';
// header('Content-Length: '.filesize(self::$cache_dir));//temprorary disable,because when cd of cloudfare is enabled it will return compress brottil size
$headers['Last-Modified'] = gmdate('D, d M Y H:i:s', $ftime) . ' GMT';
$headers['Expires'] = gmdate('D, d M Y H:i:s', $liveTime) . 'GMT';
if (Themify_Filesystem::is_file(self::$cache_dir)) {//maybe another proccess has already removed it?
$type = false; //self::get_available_gzip();temprorary disable gzip caching,because bug of cloudfare
if ($type !== false && Themify_Filesystem::is_file(self::$cache_dir . '.gz')) {
if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], $type) !== false) {
self::$cache_dir .= '.gz';
$headers['Content-Encoding'] = $type;
foreach ($headers as $name => $field_value) {
header("{$name}: {$field_value}");
do_action_ref_array('send_headers', array(&$wp));
readfile(self::$cache_dir);
Themify_Filesystem::delete(self::$cache_dir, 'f');
Themify_Filesystem::delete(self::$cache_dir . '.gz', 'f');
add_action('template_redirect', array(__CLASS__, 'template_include'), -9999999);
public static function get_current_cache(string $request = '',bool $create_dir = false):string {
$request = self::get_current_url();
return self::get_cache_folder($request, $create_dir) . md5($request);
* Initiate cache, just before page renders on frontend
* Hooked to "template_redirect"[0]
public static function template_include() {
if (!themify_is_dev_mode()) {
private static function cache_start() {
if (self::$stopCache === true || (isset($post, $post->post_password) && $post->post_password !== '') || is_user_logged_in() || is_admin() || self::$cache_dir === null || is_404() || is_search() || themify_is_ajax() || post_password_required() || is_trackback() || is_robots() || is_preview() || is_customize_preview() || themify_is_login_page() || (themify_is_woocommerce_active() && (is_checkout() || is_cart() || is_account_page()))) {
if (defined('TF_CACHE_IGNORE') && TF_CACHE_IGNORE) {
$ignore = explode(',', trim(TF_CACHE_IGNORE));
foreach ($ignore as $f) {
if (($f === 'is_shop' && themify_is_shop()) || ($f !== 'is_shop' && is_callable($f) && call_user_func($f))) {
if (false !== self::get_cache_plugins()) {
define('TF_CACHE', true);
ob_start(array(__CLASS__, 'getBuffer'));
add_action('wp_footer', array(__CLASS__, 'body_end'), 9999999);
public static function getBuffer(?string $html=''):string {
$html = ob_get_contents();
if (self::$error === false && !empty($html)) {
$html = preg_replace(array(
'/\>[^\S ]{2,}/s', // remove whitespaces after tags
'/[^\S ]{2,}\</s', // remove whitespaces before tags
'/([\t ])+/s', //shorten multiple whitespace sequences; keep new-line characters because they matter in JS!!!
'/\>[\r\n\t ]{2,}\</s', //remove empty lines (between HTML tags); cannot remove just any line-end characters because in inline JS they can matter!
), array('', '>', '<', ' ', '><'), $html);
if (self::$stopCache === false) {
$dir = rtrim(dirname(self::$cache_dir), self::SEP) . self::SEP;
if (Themify_Filesystem::mkdir($dir, true) && !is_file(self::$cache_dir)) {
//tmp file need because file_put_contents isn't atomic(another process can read not ready file),locking file(LOCK_EX) is slow and not work always,that is why we are using rename(it is atomic)
if (file_put_contents(self::$cache_dir . 'tmp', '<!--THEMIFY CACHE-->' . $html) && Themify_Filesystem::rename(self::$cache_dir . 'tmp', self::$cache_dir) !== false) {
if (false && themify_get_server() !== 'litespeed' && themify_check('setting-cache_gzip', true)) {
$func = self::get_available_gzip();
$html = call_user_func($func['f'], $html, $func['l']);
file_put_contents(self::$cache_dir . '.gz', '<!--THEMIFY CACHE-->' . $html, LOCK_EX);
Themify_Filesystem::delete(self::$cache_dir . 'tmp', 'f');
public static function body_end():void {
add_action('shutdown', array(__CLASS__, 'cache_end'), 0);
public static function cache_end():void {
public static function get_available_gzip() {
if (function_exists('brotli_compress') && ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER['SERVER_PORT']) && (int) $_SERVER['SERVER_PORT'] === 443))) {
return array('br' => array('f' => 'brotli_compress', 'l' => 10));
if (function_exists('gzdeflate')) {
return array('deflate' => array('f' => 'gzdeflate', 'l' => 8));
if (function_exists('gzcompress')) {
return array('deflate' => array('f' => 'gzcompress', 'l' => 8));
if (function_exists('gzencode')) {
return array('gzip' => array('f' => 'gzencode', 'l' => 8));
public static function create_config(array $data) {
$cache_dir = self::get_wp_content_dir();
if (Themify_Filesystem::is_writable($cache_dir)) {
if (!empty($data['setting-cache-html']) && false === self::get_cache_plugins()) {
$fw_dir = THEMIFY_DIR . self::SEP . 'cache' . self::SEP;
$fw_config = $fw_dir . 'config.php';
$cache_config = self::get_cache_config_file();
$msg = sprintf(__('Can`t copy %s to %s. Please check permission or do manually it.', 'themify'), $fw_config, $cache_config);
if (Themify_Filesystem::is_file($cache_config)) {
include_once $cache_config;
if (!empty($data['setting-cache-rule'])) {
$rules = explode(PHP_EOL, $data['setting-cache-rule']);
foreach ($rules as $i => $r) {
$rules[$i] = trim(str_replace(array('"', "'"), '', $r));
$rules = !empty($rules) ? implode('|TF|', $rules) : '';
'#TF_CACHE_FW#' => trailingslashit($fw_dir),
'#TF_CACHE_TIME#' => !empty($data['setting-cache-live']) ? ((int) $data['setting-cache-live']) : WEEK_IN_SECONDS,
'#TF_CACHE_RULES#' => $rules,
'#TF_CACHE_IGNORE#' => ''
foreach ($data as $k => $v) {
if (strpos($k, 'setting-cache-ignore_') === 0 && !empty($v)) {
$config['#TF_CACHE_IGNORE#'] = implode(',', $ignores);
$hasUpdate = (!defined('TF_CACHE_FW') || TF_CACHE_FW !== $config['#TF_CACHE_FW#']) || (!defined('TF_CACHE_RULES') || $config['#TF_CACHE_RULES#'] !== TF_CACHE_RULES) || (!defined('TF_CACHE_IGNORE') || $config['#TF_CACHE_IGNORE#'] !== TF_CACHE_IGNORE) || (!defined('TF_CACHE_TIME') || $config['#TF_CACHE_TIME#'] != TF_CACHE_TIME);
if ($hasUpdate === true) {
if (!copy($fw_config, $cache_config)) {
$content = Themify_Filesystem::get_contents($cache_config);
if (!file_put_contents($cache_config, str_replace(array_keys($config), $config, $content), LOCK_EX)) {
if (Themify_Filesystem::is_file($cache_dir . 'advanced-cache.php')) {
$content = Themify_Filesystem::get_contents($cache_dir . 'advanced-cache.php');