Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+29 -7
View File
@@ -56,10 +56,10 @@ abstract class _AbstractData
{
if( $this->exists( $key ) )
{
$value = json_decode( $this->get( $key ), TRUE );
$value = $this->decode( $this->get( $key ) );
if ( \IPS\CACHING_LOG )
{
$this->log[] = array( 'get', $key, version_compare( PHP_VERSION, '5.4.0' ) >= 0 ? json_encode( $value, JSON_PRETTY_PRINT ) : json_encode( $value ), var_export( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), TRUE ) );
$this->log[ sprintf( '%.4f', microtime(true) ) ] = array( 'get', $key, version_compare( PHP_VERSION, '5.4.0' ) >= 0 ? json_encode( $value, JSON_PRETTY_PRINT ) : json_encode( $value ), var_export( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), TRUE ) );
}
$this->_data[ $key ] = $value;
}
@@ -83,10 +83,10 @@ abstract class _AbstractData
{
if ( \IPS\CACHING_LOG )
{
$this->log[] = array( 'set', $key, version_compare( PHP_VERSION, '5.4.0' ) >= 0 ? json_encode( $value, JSON_PRETTY_PRINT ) : json_encode( $value ), var_export( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), TRUE ) );
$this->log[ sprintf( '%.4f', microtime(true) ) ] = array( 'set', $key, version_compare( PHP_VERSION, '5.4.0' ) >= 0 ? json_encode( $value, JSON_PRETTY_PRINT ) : json_encode( $value ), var_export( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), TRUE ) );
}
if ( $this->set( $key, json_encode( $value ) ) )
if ( $this->set( $key, $this->encode( $value ) ) )
{
$this->_data[ $key ] = $value;
$this->_exists[ $key ] = $key;
@@ -117,7 +117,7 @@ abstract class _AbstractData
*/
public function __isset( $key )
{
if( isset( $this->_data[ $key ] ) or in_array( $key, $this->_exists ) )
if( array_key_exists( $key, $this->_data ) or array_key_exists( $key, $this->_exists ) )
{
return TRUE;
}
@@ -130,7 +130,7 @@ abstract class _AbstractData
}
if ( \IPS\CACHING_LOG )
{
$this->log[] = array( 'check', $key, var_export( $return, TRUE ), var_export( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), TRUE ) );
$this->log[ sprintf( '%.4f', microtime(true) ) ] = array( 'check', $key, var_export( $return, TRUE ), var_export( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), TRUE ) );
}
return $return;
}
@@ -146,10 +146,32 @@ abstract class _AbstractData
{
if ( \IPS\CACHING_LOG )
{
$this->log[] = array( 'delete', $key, NULL, var_export( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), TRUE ) );
$this->log[ sprintf( '%.4f', microtime(true) ) ] = array( 'delete', $key, NULL, var_export( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), TRUE ) );
}
$this->delete( $key );
unset( $this->_data[ $key ] );
unset( $this->_exists[ $key ] );
}
/**
* Encode
*
* @param mixed $value Value
* @return string
*/
protected function encode( $value )
{
return json_encode( $value );
}
/**
* Decode
*
* @param mixed $value Value
* @return mixed
*/
protected function decode( $value )
{
return json_decode( $value, TRUE );
}
}
+82 -3
View File
@@ -68,6 +68,16 @@ abstract class _Cache extends AbstractData
return static::$instance;
}
/**
* Needs cache key check with this storage engine to maintain integrity
*
* @return boolean
*/
public function checkKeys()
{
return true;
}
/**
* Store value using cache method if available or falling back to the database
*
@@ -75,7 +85,7 @@ abstract class _Cache extends AbstractData
* @param mixed $value Value
* @param \IPS\DateTime $expire Expiration if using database
* @param bool $fallback Use database if no caching method is available?
* @return void
* @return bool
*/
public function storeWithExpire( $key, $value, \IPS\DateTime $expire, $fallback=FALSE )
{
@@ -83,14 +93,18 @@ abstract class _Cache extends AbstractData
if ( \IPS\CACHING_LOG )
{
$this->log[] = array( 'set', $key, version_compare( PHP_VERSION, '5.4.0' ) >= 0 ? json_encode( $value, JSON_PRETTY_PRINT ) : json_encode( $value ), var_export( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), TRUE ) );
$this->log[ microtime(true) ] = array( 'set', $key, version_compare( PHP_VERSION, '5.4.0' ) >= 0 ? json_encode( $value, JSON_PRETTY_PRINT ) : json_encode( $value ), var_export( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), TRUE ) );
}
if ( $this->set( $key, json_encode( $value ), $expire ) )
if ( $this->set( $key, $this->encode( $value ), $expire ) )
{
$this->_data[ $key ] = $value;
$this->_exists[ $key ] = $key;
return TRUE;
}
return FALSE;
}
/**
@@ -142,4 +156,69 @@ abstract class _Cache extends AbstractData
does not allow us to actually clear them */
\IPS\Data\Store::i()->cacheKeys = array();
}
/**
* Encryption key
*
* @return string
*/
protected function _encryptionKey()
{
$password = \IPS\Settings::i()->sql_pass;
if ( function_exists( 'openssl_digest' ) and in_array( 'sha256', openssl_get_md_methods() ) )
{
$password = openssl_digest( $password, 'sha256', TRUE );
}
return $password;
}
/**
* Encode
*
* @param mixed $value Value
* @return string
*/
protected function encode( $value )
{
$value = json_encode( $value );
if ( function_exists( 'openssl_encrypt' ) and in_array( 'aes-256-ctr', openssl_get_cipher_methods() ) )
{
$iv = \IPS\Login::generateRandomString( 16 );
$value = $iv . openssl_encrypt( $value, 'aes-256-ctr', $this->_encryptionKey(), OPENSSL_RAW_DATA, $iv );
}
return $value;
}
/**
* Decode
*
* @param mixed $value Value
* @return mixed
*/
protected function decode( $value )
{
if ( function_exists( 'openssl_encrypt' ) and in_array( 'aes-256-ctr', openssl_get_cipher_methods() ) )
{
$value = @openssl_decrypt( \substr( $value, 16 ), 'aes-256-ctr', $this->_encryptionKey(), OPENSSL_RAW_DATA, \substr( $value, 0, 16 ) );
}
return json_decode( $value, TRUE );
}
/**
* Magic Method: Isset
*
* @param string $key Key
* @return bool
*/
public function __isset( $key )
{
if ( parent::__isset( $key ) )
{
return ( is_array( $this->$key ) or $this->$key === 0 ) ? true : (bool) $this->$key;
}
return FALSE;
}
}
+8 -1
View File
@@ -148,7 +148,7 @@ class _None extends \IPS\Data\Cache
* @param mixed $value Value
* @param \IPS\DateTime $expire Expiration if using database
* @param bool $fallback Use database if no caching method is available?
* @return void
* @return bool
*/
public function storeWithExpire( $key, $value, \IPS\DateTime $expire, $fallback=FALSE )
{
@@ -159,7 +159,14 @@ class _None extends \IPS\Data\Cache
'cache_value' => json_encode( $value ),
'cache_expire' => $expire->getTimestamp()
) );
$this->_data[ $key ] = $value;
$this->_exists[ $key ] = $key;
return TRUE;
}
return FALSE;
}
/**
+71 -156
View File
@@ -22,6 +22,23 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _Redis extends \IPS\Data\Cache
{
/**
* Constructor
*
* @return void
*/
public function __construct( $configuration )
{
try
{
\IPS\Redis::i();
}
catch( \RedisException $e )
{
throw new \IPS\Data\Cache\Exception;
}
}
/**
* Server supports this method?
*
@@ -60,89 +77,28 @@ class _Redis extends \IPS\Data\Cache
}
/**
* @brief Connection resource
* Redis key
*/
protected static $links = array();
protected $_redisKey;
/**
* @brief Connection key
*/
protected $connectionKey = NULL;
/**
* @brief Connection timeout - keep low or you negate the benefits of caching
*/
protected $timeout = 2;
/**
* Constructor
* Get random string used in the keys to identify this site compared to other sites
*
* @param array $configuration Configuration
* @return void
* @param string $key
* @return string|FALSE Value from the _datastore; FALSE if key doesn't exist
*/
public function __construct( $configuration )
protected function _getRedisKey()
{
/* Figure out our connection key, as you could theoretically attempt to connect to more than one Redis server */
$this->connectionKey = md5( $configuration['server'] . ':' . $configuration['port'] );
/* If we've already attempted to establish this link, just return now */
if( isset( static::$links[ $this->connectionKey ] ) )
if ( !$this->_redisKey )
{
return;
}
/* Connect to server */
try
{
static::$links[ $this->connectionKey ] = new \Redis;
if( static::$links[ $this->connectionKey ]->connect( $configuration['server'], $configuration['port'], $this->timeout ) === FALSE )
if ( !( $this->_redisKey = \IPS\Redis::i()->get( 'redisKey' ) ) )
{
$this->resetConnection();
throw new \RedisException;
$this->_redisKey = md5( mt_rand() );
\IPS\Redis::i()->setex( 'redisKey', 604800, $this->_redisKey );
}
else
{
if( $configuration['password'] )
{
if( static::$links[ $this->connectionKey ]->auth( $configuration['password'] ) === FALSE )
{
$this->resetConnection();
throw new \RedisException;
}
}
}
if( static::$links[ $this->connectionKey ] !== NULL )
{
static::$links[ $this->connectionKey ]->setOption( \Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP );
}
/* If connection times out, connect can return TRUE and we won't know until our next attempt to talk to the server,
so we should ping now to verify we were able to connect successfully */
static::$links[ $this->connectionKey ]->ping();
register_shutdown_function( function( $object ){
try
{
if( isset( static::$links[ $object->connectionKey ] ) AND static::$links[ $object->connectionKey ] )
{
static::$links[ $object->connectionKey ]->close();
unset( static::$links[ $object->connectionKey ] );
}
}
catch( \RedisException $e ){}
}, $this );
\IPS\Log::debug( "Connected to Redis", 'cache' );
}
catch( \RedisException $e )
{
$this->resetConnection( $e );
\IPS\Log::debug( "Connection to Redis failed", 'cache' );
}
return $this->_redisKey;
}
/**
@@ -153,32 +109,23 @@ class _Redis extends \IPS\Data\Cache
*/
protected function get( $key )
{
if( isset( $this->cache[ $key ] ) )
if( array_key_exists( $key, $this->cache ) )
{
\IPS\Log::debug( "Get {$key} from Redis (already loaded)", 'cache' );
return $this->cache[ $key ];
}
if ( static::$links[ $this->connectionKey ] )
try
{
\IPS\Log::debug( "Get {$key} from Redis", 'cache' );
$this->cache[ $key ] = \IPS\Redis::i()->get( $this->_getRedisKey() . '_' . $key );
try
{
$this->cache[ $key ] = static::$links[ $this->connectionKey ]->get( \IPS\SUITE_UNIQUE_KEY . '_' . $key );
return $this->cache[ $key ];
}
catch( \RedisException $e )
{
$this->resetConnection( $e );
return FALSE;
}
return $this->cache[ $key ];
}
catch( \RedisException $e )
{
\IPS\Redis::i()->resetConnection( $e );
/* No connection */
return FALSE;
return FALSE;
}
}
/**
@@ -191,32 +138,24 @@ class _Redis extends \IPS\Data\Cache
*/
protected function set( $key, $value, \IPS\DateTime $expire = NULL )
{
if ( static::$links[ $this->connectionKey ] )
try
{
\IPS\Log::debug( "Set {$key} in Redis", 'cache' );
try
if ( $expire )
{
if ( $expire )
{
return (bool) static::$links[ $this->connectionKey ]->setex( \IPS\SUITE_UNIQUE_KEY . '_' . $key, $expire->getTimestamp() - time(), $value );
}
else
{
/* Set for 24 hours */
return (bool) static::$links[ $this->connectionKey ]->setex( \IPS\SUITE_UNIQUE_KEY . '_' . $key, 86400, $value );
}
return (bool) \IPS\Redis::i()->setex( $this->_getRedisKey() . '_' . $key, $expire->getTimestamp() - time(), $value );
}
catch( \RedisException $e )
else
{
$this->resetConnection( $e );
return FALSE;
/* Set for 24 hours */
return (bool) \IPS\Redis::i()->setex( $this->_getRedisKey() . '_' . $key, 86400, $value );
}
}
catch( \RedisException $e )
{
\IPS\Redis::i()->resetConnection( $e );
/* No connection */
return FALSE;
return FALSE;
}
}
/**
@@ -227,14 +166,11 @@ class _Redis extends \IPS\Data\Cache
*/
protected function exists( $key )
{
if( isset( $this->cache[ $key ] ) )
if( array_key_exists( $key, $this->cache ) )
{
\IPS\Log::debug( "Check exists {$key} from Redis (already loaded)", 'cache' );
return ( $this->cache[ $key ] === FALSE ) ? FALSE : TRUE;
}
\IPS\Log::debug( "Check exists {$key} from Redis", 'cache' );
/* We do a get instead of an exists() check because it will cause the cache value to be fetched and cached inline, saving another call to the server */
return ( $this->get( $key ) === FALSE ) ? FALSE : TRUE;
}
@@ -246,39 +182,16 @@ class _Redis extends \IPS\Data\Cache
* @return bool
*/
protected function delete( $key )
{
if ( static::$links[ $this->connectionKey ] )
{
try
{
\IPS\Log::debug( "Delete {$key} from Redis", 'cache' );
try
{
return (bool) static::$links[ $this->connectionKey ]->delete( \IPS\SUITE_UNIQUE_KEY . '_' . $key );
}
catch( \RedisException $e )
{
$this->resetConnection( $e );
}
return (bool) \IPS\Redis::i()->delete( $this->_getRedisKey() . '_' . $key );
}
/* No connection */
return FALSE;
}
/**
* Reset connection
*
* @param \RedisException|NULL If this was called as a result of an exception, log that to the debug log
* @return void
*/
protected function resetConnection( \RedisException $e = NULL )
{
if ( $e !== NULL )
catch( \RedisException $e )
{
\IPS\Log::debug( $e, 'redis_exception' );
\IPS\Redis::i()->resetConnection( $e );
return FALSE;
}
static::$links[ $this->connectionKey ] = NULL;
throw new \IPS\Data\Cache\Exception;
}
/**
@@ -289,17 +202,19 @@ class _Redis extends \IPS\Data\Cache
public function clearAll()
{
parent::clearAll();
if ( static::$links[ $this->connectionKey ] )
{
try
{
static::$links[ $this->connectionKey ]->flushDb();
}
catch( \RedisException $e )
{
$this->resetConnection( $e );
}
}
$this->_redisKey = md5( mt_rand() );
\IPS\Redis::i()->setex( 'redisKey', 604800, $this->_redisKey );
}
/**
* Log a page hit
*
* @param \IPS\Http\Url|NULL $url URL to log (or null)
* @return void
*/
public function logPageHit( $url=NULL )
{
return \IPS\Redis::i()->logPageHit( $url );
}
}
+63 -35
View File
@@ -58,11 +58,6 @@ abstract class _Store extends AbstractData
*/
public $log = array();
/**
* @brief Flag to determine if we should check the validity of the cache via cacheKeys. We always want this to be on unless you have an exceptional reason to bypass the check.
*/
public $cacheCheck = TRUE;
/**
* Load mutiple
* Used so if it is known that several are going to be needed, they can all be loaded into memory at the same time
@@ -83,7 +78,7 @@ abstract class _Store extends AbstractData
public function test()
{
/* Set a test key */
$key = 'test_' . md5( uniqid() );
$key = 'test_' . md5( mt_rand() );
$this->$key = '1';
/* Now clear our internal cache to ensure the value isn't returned from there */
@@ -114,13 +109,13 @@ abstract class _Store extends AbstractData
try
{
/* If caching is enabled, and this isn't the special map of hashes... */
if ( ( ! \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None ) and $key !== 'cacheKeys' )
if ( $this->useCache( $key ) )
{
if ( $this->cacheCheck === false )
if ( \IPS\Data\Cache::i()->checkKeys() === false )
{
if ( isset( \IPS\Data\Cache::i()->$key ) and \IPS\Data\Cache::i()->$key !== NULL )
{
/* We accept it may be stale, so return it */
/* Cache engine always returns current content, so return it */
return \IPS\Data\Cache::i()->$key;
}
@@ -131,13 +126,13 @@ abstract class _Store extends AbstractData
{
/* It exists in the caching engine, and we know the hash of the correct value... */
$cacheKeys = ( isset( $this->cacheKeys ) and is_array( $this->cacheKeys ) ) ? $this->cacheKeys : array();
if ( isset( $cacheKeys[ $key ] ) and isset( \IPS\Data\Cache::i()->$key ) )
if ( array_key_exists( $key, $cacheKeys ) and isset( \IPS\Data\Cache::i()->$key ) )
{
/* Get it... */
$value = \IPS\Data\Cache::i()->$key;
/* But only use it if the hash matches */
if( \IPS\Login::compareHashes( $cacheKeys[ $key ], md5( json_encode( $value ) ) ) )
if( $cacheKeys[ $key ] == md5( json_encode( $value ) ) )
{
return $value;
}
@@ -156,15 +151,18 @@ abstract class _Store extends AbstractData
$value = parent::__get( $key );
/* If caching is enabled, and this isn't the special map of hashes... */
if ( ( ! \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None ) and $key !== 'cacheKeys' )
if ( $this->useCache( $key ) )
{
/* Set it in the caching engine... */
\IPS\Data\Cache::i()->$key = $value;
/* And set the hash in the cacheKeys hash map */
$cacheKeys = ( isset( $this->cacheKeys ) and is_array( $this->cacheKeys ) ) ? $this->cacheKeys : array();
$cacheKeys[ $key ] = md5( json_encode( $value ) );
$this->cacheKeys = $cacheKeys;
if ( \IPS\Data\Cache::i()->checkKeys() === true )
{
/* And set the hash in the cacheKeys hash map */
$cacheKeys = ( isset( $this->cacheKeys ) and is_array( $this->cacheKeys ) ) ? $this->cacheKeys : array();
$cacheKeys[ $key ] = md5( json_encode( $value ) );
$this->cacheKeys = $cacheKeys;
}
}
return $value;
@@ -184,21 +182,24 @@ abstract class _Store extends AbstractData
parent::__set( $key, $value );
/* If caching is enabled, and this isn't the special map of hashes... */
if ( ( ! \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None ) and $key !== 'cacheKeys' )
if ( $this->useCache( $key ) )
{
/* Then also set it in the cache... */
\IPS\Data\Cache::i()->$key = $value;
/* And set the hash in the cacheKeys hash map */
if ( ( isset( $this->cacheKeys ) and is_array( $this->cacheKeys ) ) )
if ( \IPS\Data\Cache::i()->checkKeys() === true )
{
$cacheKeys = is_array( $this->cacheKeys ) ? $this->cacheKeys : array();
$cacheKeys[ $key ] = md5( json_encode( $value ) );
$this->cacheKeys = $cacheKeys;
}
else
{
$this->cacheKeys = array( $key => md5( json_encode( $value ) ) );
if ( ( isset( $this->cacheKeys ) and is_array( $this->cacheKeys ) ) )
{
$cacheKeys = is_array( $this->cacheKeys ) ? $this->cacheKeys : array();
$cacheKeys[ $key ] = md5( json_encode( $value ) );
$this->cacheKeys = $cacheKeys;
}
else
{
$this->cacheKeys = array( $key => md5( json_encode( $value ) ) );
}
}
}
}
@@ -212,7 +213,7 @@ abstract class _Store extends AbstractData
public function __isset( $key )
{
/* If caching is enabled, and this isn't the special map of hashes, try to get it from the cache store... */
if ( ( ! \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None ) and $key !== 'cacheKeys' )
if ( $this->useCache( $key ) )
{
/* Does it exist in the caching engine? */
if ( isset( \IPS\Data\Cache::i()->$key ) )
@@ -220,15 +221,15 @@ abstract class _Store extends AbstractData
/* Get it... */
$value = \IPS\Data\Cache::i()->$key;
if ( $this->cacheCheck === false )
if ( \IPS\Data\Cache::i()->checkKeys() === false )
{
/* We accept it may be stale, so return it */
/* Cache engine always returns current content, so return it */
return TRUE;
}
/* Only use it if the hash matches */
$cacheKeys = ( isset( $this->cacheKeys ) and is_array( $this->cacheKeys ) ) ? $this->cacheKeys : array();
if ( isset( $cacheKeys[ $key ] ) and \IPS\Login::compareHashes( $cacheKeys[ $key ], md5( json_encode( $value ) ) ) )
if ( isset( $cacheKeys[ $key ] ) and $cacheKeys[ $key ] == md5( json_encode( $value ) ) )
{
return TRUE;
}
@@ -251,15 +252,42 @@ abstract class _Store extends AbstractData
parent::__unset( $key );
/* If caching is enabled, and this isn't the special map of hashes, remove it from the cache store... */
if ( ( ! \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None ) and $key !== 'cacheKeys' )
if ( $this->useCache( $key ) )
{
/* Remove it from the cache store... */
unset( \IPS\Data\Cache::i()->$key );
/* And from the special map of hashes */
$cacheKeys = is_array( $this->cacheKeys ) ? $this->cacheKeys : array();
unset( $cacheKeys[ $key ] );
$this->cacheKeys = $cacheKeys;
if ( \IPS\Data\Cache::i()->checkKeys() === true )
{
/* And from the special map of hashes */
try
{
$cacheKeys = is_array( $this->cacheKeys ) ? $this->cacheKeys : array();
}
/* If the cache doesn't exist we shouldn't error out with an uncaught OutOfRangeException */
catch( \OutOfRangeException $e )
{
$cacheKeys = array();
}
if( isset( $cacheKeys[ $key ] ) )
{
unset( $cacheKeys[ $key ] );
}
$this->cacheKeys = $cacheKeys;
}
}
}
/**
* Check the cache for this key?
*
* @param string $key The store key
* @return boolean
*/
protected function useCache( $key )
{
return ( \IPS\CACHE_METHOD != \IPS\STORE_METHOD and ( ! \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None ) and $key !== 'cacheKeys' );
}
}
+1 -1
View File
@@ -56,7 +56,7 @@ class _Database extends \IPS\Data\Store
public function __construct()
{
/* When caching is enabled, we only need cacheKeys, the rest will come from the cache */
if ( \IPS\CACHE_METHOD !== 'None' )
if ( !\IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None )
{
$this->initLoad = array( 'cacheKeys' );
}
+6
View File
@@ -75,6 +75,12 @@ class _FileSystem extends \IPS\Data\Store
public function __construct( $configuration )
{
$this->_path = rtrim( str_replace( '{root}', \IPS\ROOT_PATH, $configuration['path'] ), '/' );
/* Fallback for an invalid path */
if( !$this->_path )
{
$this->_path = \IPS\ROOT_PATH . '/datastore';
}
}
/**
+174
View File
@@ -0,0 +1,174 @@
<?php
/**
* @brief Redis Storage Class
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 06 October 2017
*/
namespace IPS\Data\Store;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Redis Storage Class
*/
class _Redis extends \IPS\Data\Store
{
/**
* Server supports this method?
*
* @return bool
*/
public static function supported()
{
return class_exists('Redis');
}
/**
* Redis key
*/
protected $_redisKey;
/**
* Get random string used in the keys to identify this site compared to other sites
*
* @param string $key
* @return string|FALSE Value from the _datastore; FALSE if key doesn't exist
*/
protected function _getRedisKey()
{
if ( !$this->_redisKey )
{
if ( !( $this->_redisKey = \IPS\Redis::i()->get( 'redisKey_store' ) ) )
{
$this->_redisKey = md5( mt_rand() );
\IPS\Redis::i()->setex( 'redisKey_store', 604800, $this->_redisKey );
}
}
return $this->_redisKey . '_str_';
}
/**
* @brief Cache
*/
protected static $cache = array();
/**
* Abstract Method: Get
*
* @param string $key
* @return string|FALSE Value from the _datastore; FALSE if key doesn't exist
*/
public function get( $key )
{
if( array_key_exists( $key, static::$cache ) )
{
return static::$cache[ $key ];
}
try
{
$return = \IPS\Redis::i()->get( $this->_getRedisKey() . '_' . $key );
if ( $return !== FALSE )
{
static::$cache[ $key ] = \IPS\Redis::i()->decode( $return );
return static::$cache[ $key ];
}
else
{
throw new \UnderflowException;
}
}
catch( \RedisException $e )
{
\IPS\Redis::i()->resetConnection( $e );
throw new \UnderflowException;
}
}
/**
* Abstract Method: Set
*
* @param string $key Key
* @param string $value Value
* @return bool
*/
public function set( $key, $value )
{
try
{
return (bool) \IPS\Redis::i()->setex( $this->_getRedisKey() . '_' . $key, 604800, \IPS\Redis::i()->encode( $value ) );
}
catch( \RedisException $e )
{
\IPS\Redis::i()->resetConnection( $e );
return FALSE;
}
}
/**
* Abstract Method: Exists?
*
* @param string $key Key
* @return bool
*/
public function exists( $key )
{
if( array_key_exists( $key, static::$cache ) )
{
return ( static::$cache[ $key ] === FALSE ) ? FALSE : TRUE;
}
/* We do a get instead of an exists() check because it will cause the cache value to be fetched and cached inline, saving another call to the server */
try
{
return ( $this->get( $key ) === FALSE ) ? FALSE : TRUE;
}
catch ( \UnderflowException $e )
{
return FALSE;
}
}
/**
* Abstract Method: Delete
*
* @param string $key Key
* @return bool
*/
public function delete( $key )
{
try
{
return (bool) \IPS\Redis::i()->delete( $this->_getRedisKey() . '_' . $key );
}
catch( \RedisException $e )
{
\IPS\Redis::i()->resetConnection( $e );
return FALSE;
}
}
/**
* Abstract Method: Clear All
*
* @return void
*/
public function clearAll()
{
$this->_redisKey = md5( mt_rand() );
\IPS\Redis::i()->setex( 'redisKey_store', 604800, $this->_redisKey );
}
}