/content/plugins/w3-total-cache/Cache_File.php
https://gitlab.com/karlen/ayo_wp · PHP · 455 lines · 246 code · 84 blank · 125 comment · 55 complexity · aafa778f56ec73edd4a84cdca51a1583 MD5 · raw file
- <?php
- namespace W3TC;
- /**
- * class Cache_File
- */
- class Cache_File extends Cache_Base {
- /**
- * Path to cache dir
- *
- * @var string
- */
- protected $_cache_dir = '';
- /**
- * Directory to flush
- *
- * @var string
- */
- protected $_flush_dir = '';
- /**
- * Exclude files
- *
- * @var array
- */
- protected $_exclude = array();
- /**
- * Flush time limit
- *
- * @var int
- */
- protected $_flush_timelimit = 0;
- /**
- * File locking
- *
- * @var boolean
- */
- protected $_locking = false;
- /**
- * If path should be generated based on wp_hash
- *
- * @var bool
- */
- protected $_use_wp_hash = false;
- /**
- * Constructor
- *
- * @param array $config
- */
- function __construct( $config = array() ) {
- parent::__construct( $config );
- if ( isset( $config['cache_dir'] ) )
- $this->_cache_dir = trim( $config['cache_dir'] );
- else
- $this->_cache_dir = Util_Environment::cache_blog_dir( $config['section'], $config['blog_id'] );
- $this->_exclude = isset( $config['exclude'] ) ? (array) $config['exclude'] : array();
- $this->_flush_timelimit = isset( $config['flush_timelimit'] ) ? (int) $config['flush_timelimit'] : 180;
- $this->_locking = isset( $config['locking'] ) ? (boolean) $config['locking'] : false;
- if ( isset( $config['flush_dir'] ) )
- $this->_flush_dir = $config['flush_dir'];
- else {
- if ( $config['blog_id'] <= 0 && !isset( $config['cache_dir'] ) ) {
- // clear whole section if we operate on master cache
- // and in a mode when cache_dir not strictly specified
- $this->_flush_dir = Util_Environment::cache_dir( $config['section'] );
- } else
- $this->_flush_dir = $this->_cache_dir;
- }
- if ( isset( $config['use_wp_hash'] ) && $config['use_wp_hash'] )
- $this->_use_wp_hash = true;
- }
- /**
- * Adds data
- *
- * @param string $key
- * @param mixed $var
- * @param integer $expire
- * @param string $group Used to differentiate between groups of cache values
- * @return boolean
- */
- function add( $key, &$var, $expire = 0, $group = '' ) {
- if ( $this->get( $key, $group ) === false ) {
- return $this->set( $key, $var, $expire, $group );
- }
- return false;
- }
- /**
- * Sets data
- *
- * @param string $key
- * @param mixed $var
- * @param integer $expire
- * @param string $group Used to differentiate between groups of cache values
- * @return boolean
- */
- function set( $key, $var, $expire = 0, $group = '' ) {
- $fp = $this->fopen_write( $key, $group, 'wb' );
- if ( !$fp )
- return false;
- if ( $this->_locking )
- @flock( $fp, LOCK_EX );
- if ( $expire <= 0 || $expire > W3TC_CACHE_FILE_EXPIRE_MAX )
- $expire = W3TC_CACHE_FILE_EXPIRE_MAX;
- $expires_at = time() + $expire;
- @fputs( $fp, pack( 'L', $expires_at ) );
- @fputs( $fp, '<?php exit; ?>' );
- @fputs( $fp, @serialize( $var ) );
- @fclose( $fp );
- if ( $this->_locking )
- @flock( $fp, LOCK_UN );
- return true;
- }
- /**
- * Returns data
- *
- * @param string $key
- * @param string $group Used to differentiate between groups of cache values
- * @return mixed
- */
- function get_with_old( $key, $group = '' ) {
- list( $data, $has_old_data ) = $this->_get_with_old_raw( $key, $group );
- if ( !empty( $data ) )
- $data_unserialized = @unserialize( $data );
- else
- $data_unserialized = $data;
- return array( $data_unserialized, $has_old_data );
- }
- private function _get_with_old_raw( $key, $group = '' ) {
- $has_old_data = false;
- $storage_key = $this->get_item_key( $key );
- $path = $this->_cache_dir . DIRECTORY_SEPARATOR .
- ( $group ? $group . DIRECTORY_SEPARATOR : '' ) .
- $this->_get_path( $storage_key );
- if ( !is_readable( $path ) )
- return array( null, $has_old_data );
- $fp = @fopen( $path, 'rb' );
- if ( !$fp )
- return array( null, $has_old_data );
- if ( $this->_locking )
- @flock( $fp, LOCK_SH );
- $expires_at = @fread( $fp, 4 );
- $data = null;
- if ( $expires_at !== false ) {
- list( , $expires_at ) = @unpack( 'L', $expires_at );
- if ( time() > $expires_at ) {
- if ( $this->_use_expired_data ) {
- // update expiration so other threads will use old data
- $fp2 = @fopen( $path, 'cb' );
- if ( $fp2 ) {
- @fputs( $fp2, pack( 'L', time() + 30 ) );
- @fclose( $fp2 );
- }
- $has_old_data = true;
- }
- } else {
- $data = '';
- while ( !@feof( $fp ) ) {
- $data .= @fread( $fp, 4096 );
- }
- $data = substr( $data, 14 );
- }
- }
- if ( $this->_locking )
- @flock( $fp, LOCK_UN );
- @fclose( $fp );
- return array( $data, $has_old_data );
- }
- /**
- * Replaces data
- *
- * @param string $key
- * @param mixed $var
- * @param integer $expire
- * @param string $group Used to differentiate between groups of cache values
- * @return boolean
- */
- function replace( $key, &$var, $expire = 0, $group = '' ) {
- if ( $this->get( $key, $group ) !== false ) {
- return $this->set( $key, $var, $expire, $group );
- }
- return false;
- }
- /**
- * Deletes data
- *
- * @param string $key
- * @param string $group Used to differentiate between groups of cache values
- * @return boolean
- */
- function delete( $key, $group = '' ) {
- $storage_key = $this->get_item_key( $key );
- $path = $this->_cache_dir . DIRECTORY_SEPARATOR .
- ( $group ? $group . DIRECTORY_SEPARATOR : '' ) .
- $this->_get_path( $storage_key );
- if ( !file_exists( $path ) )
- return true;
- if ( $this->_use_expired_data ) {
- $fp = @fopen( $path, 'cb' );
- if ( $fp ) {
- if ( $this->_locking )
- @flock( $fp, LOCK_EX );
- @fputs( $fp, pack( 'L', 0 ) ); // make it expired
- @fclose( $fp );
- if ( $this->_locking )
- @flock( $fp, LOCK_UN );
- return true;
- }
- }
- return @unlink( $path );
- }
- /**
- * Key to delete, deletes .old and primary if exists.
- *
- * @param string $key
- *
- * @return bool
- */
- function hard_delete( $key ) {
- $key = $this->get_item_key( $key );
- $path = $this->_cache_dir . DIRECTORY_SEPARATOR . $this->_get_path( $key );
- return @unlink( $path );
- }
- /**
- * Flushes all data
- *
- * @param string $group Used to differentiate between groups of cache values
- * @return boolean
- */
- function flush( $group = '' ) {
- @set_time_limit( $this->_flush_timelimit );
- $flush_dir = $group ?
- $this->_cache_dir . DIRECTORY_SEPARATOR . $group .
- DIRECTORY_SEPARATOR :
- $this->_flush_dir;
- Util_File::emptydir( $flush_dir, $this->_exclude );
- return true;
- }
- /**
- * Returns modification time of cache file
- *
- * @param integer $key
- * @param string $group Used to differentiate between groups of cache values
- * @return boolean|string
- */
- function mtime( $key, $group = '' ) {
- $path =
- $this->_cache_dir . DIRECTORY_SEPARATOR .
- ( $group ? $group . DIRECTORY_SEPARATOR : '' ) .
- $this->_get_path( $key );
- if ( file_exists( $path ) ) {
- return @filemtime( $path );
- }
- return false;
- }
- /**
- * Returns file path for key
- *
- * @param string $key
- * @return string
- */
- function _get_path( $key ) {
- if ( $this->_use_wp_hash && function_exists( 'wp_hash' ) )
- $hash = wp_hash( $key );
- else
- $hash = md5( $key );
- $path = sprintf( '%s/%s/%s.php', substr( $hash, 0, 3 ), substr( $hash, 3, 3 ), $hash );
- return $path;
- }
- public function get_stats_size( $timeout_time ) {
- $size = array(
- 'bytes' => 0,
- 'items' => 0,
- 'timeout_occurred' => false
- );
- $size = $this->dirsize( $this->_cache_dir, $size, $timeout_time );
- return $size;
- }
- private function dirsize( $path, $size, $timeout_time ) {
- $dir = @opendir( $path );
- if ( $dir ) {
- while ( !$size['timeout_occurred'] && ( $entry = @readdir( $dir ) ) !== false ) {
- if ( $entry == '.' || $entry == '..' ) {
- continue;
- }
- $full_path = $path . DIRECTORY_SEPARATOR . $entry;
- if ( @is_dir( $full_path ) ) {
- $size = $this->dirsize( $full_path, $size, $timeout_time );
- } else {
- $size['bytes'] += @filesize( $full_path );
- // dont check time() for each file, quite expensive
- $size['items']++;
- if ( $size['items'] % 1000 == 0 )
- $size['timeout_occurred'] |= ( time() > $timeout_time );
- }
- }
- @closedir( $dir );
- }
- return $size;
- }
- /**
- * Used to replace as atomically as possible known value to new one
- */
- public function set_if_maybe_equals( $key, $old_value, $new_value ) {
- // cant guarantee atomic action here, filelocks fail often
- $value = $this->get( $key );
- if ( isset( $old_value['content'] ) &&
- $value['content'] != $old_value['content'] )
- return false;
- return $this->set( $key, $new_value );
- }
- /**
- * Use key as a counter and add integet value to it
- */
- public function counter_add( $key, $value ) {
- if ( $value == 0 )
- return true;
- $fp = $this->fopen_write( $key, '', 'a' );
- if ( !$fp )
- return false;
- // use "x" to store increment, since it's most often case
- // and it will save 50% of size if only increments are used
- if ( $value == 1 )
- @fputs( $fp, 'x' );
- else
- @fputs( $fp, ' ' . (int)$value );
- @fclose( $fp );
- return true;
- }
- /**
- * Use key as a counter and add integet value to it
- */
- public function counter_set( $key, $value ) {
- $fp = $this->fopen_write( $key, '', 'wb' );
- if ( !$fp )
- return false;
- $expire = W3TC_CACHE_FILE_EXPIRE_MAX;
- $expires_at = time() + $expire;
- @fputs( $fp, pack( 'L', $expires_at ) );
- @fputs( $fp, '<?php exit; ?>' );
- @fputs( $fp, (int)$value );
- @fclose( $fp );
- return true;
- }
- /**
- * Get counter's value
- */
- public function counter_get( $key ) {
- list( $value, $old_data ) = $this->_get_with_old_raw( $key );
- if ( empty( $value ) )
- return 0;
- $original_length = strlen( $value );
- $cut_value = str_replace( 'x', '', $value );
- $count = $original_length - strlen( $cut_value );
- // values more than 1 are stored as <space>value
- $a = explode( ' ', $cut_value );
- foreach ( $a as $counter_value )
- $count += (int)$counter_value;
- return $count;
- }
- private function fopen_write( $key, $group, $mode ) {
- $storage_key = $this->get_item_key( $key );
- $sub_path = $this->_get_path( $storage_key );
- $path = $this->_cache_dir . DIRECTORY_SEPARATOR .
- ( $group ? $group . DIRECTORY_SEPARATOR : '' ) . $sub_path;
- $dir = dirname( $path );
- if ( !@is_dir( $dir ) ) {
- if ( !Util_File::mkdir_from( $dir, W3TC_CACHE_DIR ) )
- return false;
- }
- $fp = @fopen( $path, $mode );
- return $fp;
- }
- }