View file File name : OptimizerCache.php Content :<?php namespace TenWebOptimizer; /* * Handles disk-cache-related operations. */ if (!defined('ABSPATH')) { exit; } class OptimizerCache { /** * Cache filename. * * @var string */ private $filename; /** * Cache directory path (with a trailing slash). * * @var string */ private $cachedir; /** * Whether gzipping is done by the web server or us. * True => we don't gzip, the web server does it. * False => we do it ourselves. * * @var bool */ private $nogzip; private $hashes; private $ext; private $media; /** * Ctor. * * @param string $md5 hash * @param string $ext extension */ public function __construct($post_id = null, $ext = 'php', $media = 'all', $name_prefix = '') { if (!isset($post_id)) { $post_id = OptimizerUtils::get_current_post_info(); } if (empty($name_prefix)) { $name_prefix = 'aggregated'; } $this->media = $media; $this->cachedir = TWO_CACHE_DIR; $this->nogzip = TWO_CACHE_NOGZIP; $this->ext = $ext; if (!$this->nogzip) { $this->filename = TWO_CACHEFILE_PREFIX . $post_id . 'gzip.php'; } else { if (in_array($ext, ['js', 'css'])) { if ($media !== 'all') { if (!empty($media)) { $name_prefix .= '_' . md5($media) . '_delay'; } else { $name_prefix .= '_delay'; } } $this->filename = $ext . '/' . TWO_CACHEFILE_PREFIX . $post_id . '_' . $name_prefix . '.min.' . $ext; } elseif ($ext === 'critical') { $this->filename = $ext . '/' . TWO_CACHEFILE_PREFIX . $post_id . '_' . $name_prefix . '.css'; } elseif ($ext === 'font') { $this->filename = 'critical/' . TWO_CACHEFILE_PREFIX . $post_id . '_' . $name_prefix . '.json'; } else { $this->filename = TWO_CACHEFILE_PREFIX . $post_id . '.' . $ext; } } } //todo remove first version cache logic /** * Check whether it is a GET REQUEST */ public static function isGetRequest() { return isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'GET'; } /** * Returns true if the cached file exists on disk. * * @return bool */ public function check() { static $files = []; $files[] = $this->filename; if ( !is_dir($this->cachedir) && !mkdir($concurrentDirectory = $this->cachedir, 0777, true) && // phpcs:ignore !is_dir($concurrentDirectory) && !is_writable($concurrentDirectory) // phpcs:ignore ) { return false; } file_put_contents($this->cachedir . '_all_cache_files.txt', json_encode($files)); // phpcs:ignore return file_exists($this->cachedir . $this->filename); } /** * Returns cache contents if they exist, false otherwise. * * @return string|false */ public function retrieve() { if ($this->check()) { if (false == $this->nogzip) { return file_get_contents($this->cachedir . $this->filename . '.none'); } return file_get_contents($this->cachedir . $this->filename); // phpcs:ignore } return false; } /** * Stores given $data in cache. * * @param string $data data to cache * @param string $mime mimetype * * @return void */ public function cache($data, $mime) { self::check_and_create_dirs(); if ($this->nogzip === false) { // We handle gzipping ourselves. $file = 'default.php'; $phpcode = file_get_contents(TENWEB_SO_PLUGIN_DIR . 'config/' . $file); $phpcode = str_replace(['%%CONTENT%%', 'exit;'], [$mime, ''], $phpcode); @file_put_contents($this->cachedir . $this->filename, $phpcode); // phpcs:ignore @file_put_contents($this->cachedir . $this->filename . '.none', $data); // phpcs:ignore } else { // Write code to cache without doing anything else. @file_put_contents($this->cachedir . $this->filename, $data); // phpcs:ignore } if (!empty($this->hashes)) { $cacheFile = $this->cachedir . '_cached.json'; $oldData = ''; if (file_exists($cacheFile)) { $oldData = json_decode(file_get_contents($cacheFile), true); // phpcs:ignore } if (empty($oldData)) { $oldData = []; } $oldData[$this->filename] = ['media' => $this->media, 'hashes' => $this->hashes]; @file_put_contents($cacheFile, json_encode($oldData)); // phpcs:ignore } } public static function getFileCacheSructure() { $cacheFile = TWO_CACHE_DIR . '_cached.json'; if (file_exists($cacheFile)) { return json_decode(file_get_contents($cacheFile), true); // phpcs:ignore } return []; } public static function filterThroughCache($scripts) { $cachedFiles = OptimizerCache::getFileCacheSructure(); $result = [ 'code' => [], 'scripts' => $scripts ]; foreach ($cachedFiles as $key => $files) { $scriptsToRemove = []; if (isset($files['hashes'])) { foreach ($files['hashes'] as $i => $file) { if (isset($scripts[$file])) { $scriptsToRemove[] = $file; unset($files['hashes'][$i]); } } if (empty($files['hashes'])) { if (empty($result['code'][$files['media']])) { $result['code'][$files['media']] = ''; } $result['code'][$files['media']] .= file_get_contents(TWO_CACHE_DIR . $key); // phpcs:ignore $result['scripts'] = array_diff_key($result['scripts'], array_flip($scriptsToRemove)); } } } return $result; } /** * Get cache filename. * * @return string */ public function getname($flag = false) { if ($flag) { return $this->filename; } $date = time(); return $this->filename . '?date=' . $date; } protected static function is_valid_cache_file($dir, $file) { //check if is valid file return '.' !== $file && '..' !== $file && false !== strpos($file, TWO_CACHEFILE_PREFIX) && is_file($dir . $file); } /** * Clears contents of TWO_CACHE_DIR. * * @return void */ protected static function clear_cache_classic() { $contents = self::get_cache_contents(); foreach ($contents as $name => $files) { $dir = rtrim(TWO_CACHE_DIR . $name, '/') . '/'; foreach ($files as $file) { if (self::is_valid_cache_file($dir, $file)) { @unlink($dir . $file); // @codingStandardsIgnoreLine } } } @unlink(TWO_CACHE_DIR . '/.htaccess'); // @codingStandardsIgnoreLine } /** * Recursively deletes the specified pathname (file/directory) if possible. * Returns true on success, false otherwise. * * @param string $pathname pathname to remove * * @return bool */ protected static function rmdir($pathname) { $files = self::get_dir_contents($pathname); foreach ($files as $file) { $path = $pathname . '/' . $file; if (is_dir($path)) { self::rmdir($path); } else { unlink($path); // phpcs:ignore } } return rmdir($pathname); // phpcs:ignore } /** * Clears contents of TWO_CACHE_DIR by renaming the current * cache directory into a new one with a unique name and then * re-creating the default (empty) cache directory. * * @return bool returns true when everything is done successfully, false otherwise */ protected static function clear_cache_via_rename() { $ok = false; $dir = self::get_pathname_base(); $new_name = self::get_unique_name(); // Makes sure the new pathname is on the same level... $new_pathname = dirname($dir) . '/' . $new_name; $renamed = @rename($dir, $new_pathname); // @codingStandardsIgnoreLine return $ok; } /** * Returns a (hopefully) unique new cache folder name for renaming purposes. * * @return string */ protected static function get_unique_name() { $prefix = self::get_advanced_cache_clear_prefix(); return uniqid($prefix, true); } /** * Get cache prefix name used in advanced cache clearing mode. * * @return string */ protected static function get_advanced_cache_clear_prefix() { $pathname = self::get_pathname_base(); $basename = basename($pathname); return $basename . '-'; } /** * Returns an array of file and directory names found within * the given $pathname without '.' and '..' elements. * * @param string $pathname pathname * * @return array */ protected static function get_dir_contents($pathname) { return array_slice(scandir($pathname), 2); } /** * Wipes directories which were created as part of the fast cache clearing * routine (which renames the current cache directory into a new one with * a custom-prefixed unique name). * * @return bool */ public static function delete_advanced_cache_clear_artifacts() { $dir = self::get_pathname_base(); $prefix = self::get_advanced_cache_clear_prefix(); $parent = dirname($dir); $ok = false; // Returns the list of files without '.' and '..' elements. $files = self::get_dir_contents($parent); if (is_array($files) && !empty($files)) { foreach ($files as $file) { $path = $parent . '/' . $file; $prefixed = (false !== strpos($path, $prefix)); // Removing only our own (prefixed) directories... if (is_dir($path) && $prefixed) { $ok = self::rmdir($path); } } } return $ok; } public static function get_path($getBase = true) { $pathname = self::get_pathname_base(); if (is_multisite()) { $blog_id = get_current_blog_id(); $pathname .= $blog_id . '/'; } if ($getBase) { return $pathname; } return $pathname; } /** * Returns the base path of our cache directory. * * @return string */ protected static function get_pathname_base() { return WP_CONTENT_DIR . TENWEB_SO_CACHE_CHILD_DIR; } protected static function get_cache_contents() { $contents = []; foreach (['', 'js', 'css'] as $dir) { $contents[$dir] = scandir(TWO_CACHE_DIR . $dir); } return $contents; } /** * Performs a scan of cache directory contents and returns an array * with 3 values: count, size, timestamp. * count = total number of found files * size = total filesize (in bytes) of found files * timestamp = unix timestamp when the scan was last performed/finished. * * @return array */ protected static function stats_scan() { $count = 0; $size = 0; // Scan everything in our cache directories. foreach (self::get_cache_contents() as $name => $files) { $dir = rtrim(TWO_CACHE_DIR . $name, '/') . '/'; foreach ($files as $file) { if (self::is_valid_cache_file($dir, $file)) { if (TWO_CACHE_NOGZIP && (false !== strpos($file, '.js') || false !== strpos($file, '.css') || false !== strpos($file, '.img') || false !== strpos($file, '.txt'))) { // Web server is gzipping, we count .js|.css|.img|.txt files. $count++; } elseif (!TWO_CACHE_NOGZIP && false !== strpos($file, '.none')) { // We are gzipping ourselves via php, counting only .none files. $count++; } $size += filesize($dir . $file); } } } return [$count, $size, time()]; } /** * Checks if cache dirs exist and create if not. * Returns false if not succesful. * * @return bool */ public static function check_and_create_dirs() { if (!defined('TWO_CACHE_DIR')) { // We didn't set a cache. return false; } foreach (['', 'js', 'css', 'critical'] as $dir) { if (!self::check_cache_dir(TWO_CACHE_DIR . $dir)) { return false; } } return true; } /** * Ensures the specified `$dir` exists and is writeable. * Returns false if that's not the case. * * @param string $dir directory to check/create * * @return bool */ protected static function check_cache_dir($dir) { // Try creating the dir if it doesn't exist. if (!file_exists($dir)) { if (!mkdir($dir, 0777, true) && !is_dir($dir)) { // phpcs:ignore return false; } // @codingStandardsIgnoreLine if (!file_exists($dir)) { return false; } } // If we still cannot write, bail. if (!is_writable($dir)) { // phpcs:ignore return false; } // Create an index.html in there to avoid prying eyes! $idx_file = rtrim($dir, '/\\') . '/index.html'; if (!is_file($idx_file)) { @file_put_contents($idx_file, '<html><head><meta name="robots" content="noindex, nofollow"></head><body></body></html>'); // phpcs:ignore } return true; } }