Version 4.5.0
This commit is contained in:
1 parent
1a0c7fd3c2
commit
f79dcf067a
3791 files changed
+248032
-76372
No files matched your search
+94
-105
@@ -11,7 +11,7 @@
|
||||
namespace IPS;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -51,24 +51,27 @@ class _Redis
|
||||
* @brief Unpack the config once
|
||||
*/
|
||||
protected static $config = NULL;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Can we encrypt?
|
||||
* Writes made
|
||||
*/
|
||||
protected $canEncrypt = FALSE;
|
||||
protected static $writes = array();
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @param NULL|array $configuration Configuration to use (NULL to use \IPS\REDIS_CONFIG or \IPS\CACHE_CONFIG)
|
||||
* @param NULL|string $identifier Identifier (to support multiple instances)
|
||||
* @return \Redis
|
||||
*/
|
||||
public static function i( $configuration=NULL, $identifier=NULL )
|
||||
{
|
||||
if ( static::$config === NULL )
|
||||
{
|
||||
static::$config = $configuration ?: json_decode( \IPS\CACHE_CONFIG, true );
|
||||
$config = ( \defined( '\IPS\REDIS_CONFIG' ) and \IPS\REDIS_CONFIG !== NULL ) ? \IPS\REDIS_CONFIG : \IPS\CACHE_CONFIG;
|
||||
static::$config = $configuration ?: json_decode( $config, true );
|
||||
}
|
||||
|
||||
|
||||
$identifier = $identifier ? $identifier : '_MAIN';
|
||||
|
||||
if ( ! isset( static::$multitons[ $identifier ] ) )
|
||||
@@ -81,23 +84,34 @@ class _Redis
|
||||
|
||||
/* Return */
|
||||
return static::$multitons[ $identifier ];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Destructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
public function __destruct()
|
||||
{
|
||||
$this->canEncrypt = \IPS\REDIS_ENCRYPT === TRUE and function_exists( 'openssl_encrypt' ) and in_array( 'aes-256-ctr', openssl_get_cipher_methods() );
|
||||
if ( \IPS\DEBUG_LOG and \count( static::$writes ) > 50 )
|
||||
{
|
||||
$slice = implode( ', ', \array_slice( static::$writes, 0, 50 ) );
|
||||
\IPS\Log::debug( "Large number of Redis writes: " . $slice, 'redis_writes' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Chosen reader for this session
|
||||
*/
|
||||
protected static $reader = NULL;
|
||||
|
||||
/**
|
||||
* Connect to Redis
|
||||
*
|
||||
* @param string $identifier
|
||||
* @param string|NULL $identifier Identifier
|
||||
* @return \Redis
|
||||
* @throws \RedisException If connection cannot be established
|
||||
* @throws \BadMethodCallException If Redis PHP extension is not installed
|
||||
*/
|
||||
public function connection( $identifier=NULL )
|
||||
{
|
||||
@@ -116,9 +130,13 @@ class _Redis
|
||||
}
|
||||
else if ( $identifier === 'read' )
|
||||
{
|
||||
$randomReaderIndex = rand( 0, count( static::$config['read'] ) - 1 );
|
||||
$useConfig = static::$config['read'][ $randomReaderIndex ];
|
||||
$identifier = 'read' . $randomReaderIndex;
|
||||
if ( static::$reader === NULL )
|
||||
{
|
||||
static::$reader = rand( 0, \count( static::$config['read'] ) - 1 );
|
||||
}
|
||||
|
||||
$useConfig = static::$config['read'][ static::$reader ];
|
||||
$identifier = 'read' . static::$reader;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -173,7 +191,7 @@ class _Redis
|
||||
static::$log[ sprintf( '%.4f', microtime(true) ) ] = array( 'redis', "Redis connected (" . $identifier . ' ' . $useConfig['server'] . ")" );
|
||||
}
|
||||
|
||||
if ( count( static::$connections ) === 1 )
|
||||
if ( \count( static::$connections ) === 1 )
|
||||
{
|
||||
register_shutdown_function( function( $object ){
|
||||
try
|
||||
@@ -199,6 +217,12 @@ class _Redis
|
||||
}
|
||||
catch( \RedisException $e )
|
||||
{
|
||||
/* Unset this connection */
|
||||
if ( isset( static::$connections[ $identifier ] ) )
|
||||
{
|
||||
unset( static::$connections[ $identifier ] );
|
||||
}
|
||||
|
||||
$this->resetConnection( $e );
|
||||
}
|
||||
}
|
||||
@@ -214,19 +238,29 @@ class _Redis
|
||||
/**
|
||||
* Call methods
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return mixed
|
||||
* @param string $method Method
|
||||
* @param mixed $args Arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call( $method, $args )
|
||||
{
|
||||
if ( method_exists( 'Redis', $method ) )
|
||||
{
|
||||
$type = ( \stristr( $method, 'get' ) or \stristr( $method, 'RevRange' ) ) ? 'read' : 'write';
|
||||
$return = call_user_func_array( array( $this->connection( $type ), $method ), $args );
|
||||
$return = $this->connection( $type )->$method( ...$args );
|
||||
|
||||
if ( \IPS\REDIS_LOG )
|
||||
if ( $type === 'write' and \count( $args ) )
|
||||
{
|
||||
static::$writes[] = $args[0];
|
||||
}
|
||||
|
||||
if ( \IPS\REDIS_LOG and \count( $args ) )
|
||||
{
|
||||
if ( preg_match( '#^[a-f0-9]{32}_str__#', $args[0] ) )
|
||||
{
|
||||
$args[0] = preg_replace( '#^[a-f0-9]{32}_str__#', '[DATASTORE] ', $args[0] );
|
||||
}
|
||||
|
||||
static::$log[ sprintf( '%.4f', microtime(true) ) ] = array( 'redis', "({$type}) {$method} " . $args[0], json_encode( $args ) );
|
||||
}
|
||||
|
||||
@@ -238,9 +272,9 @@ class _Redis
|
||||
* Add one or more members to a sorted set or update its score if it already exists
|
||||
* Overloaded here so it can add a TTL to prevent permanent keys
|
||||
*
|
||||
* @param string $key
|
||||
* @param float $score
|
||||
* @param string $value
|
||||
* @param string $key Key
|
||||
* @param float $score Score
|
||||
* @param string $value Value
|
||||
* @param int|NULL $ttl TTL in seconds
|
||||
* @return 1 if the element is added. 0 otherwise.
|
||||
*/
|
||||
@@ -262,8 +296,8 @@ class _Redis
|
||||
* Fills in a whole hash. Non-string values are converted to string, using the standard (string) cast. NULL values are stored as empty strings.
|
||||
* Overloaded here so it can add a TTL to prevent permanent keys
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $value
|
||||
* @param string $key Key
|
||||
* @param array $value Value
|
||||
* @param int|NULL $ttl TTL in seconds
|
||||
* @return boolean
|
||||
*/
|
||||
@@ -285,9 +319,9 @@ class _Redis
|
||||
* Set the string value in argument as value of the key, with a time to live
|
||||
* Overloaded here so it can be logged
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $key Key
|
||||
* @param int|NULL $ttl TTL in seconds
|
||||
* @param string $value
|
||||
* @param string $value Value
|
||||
* @return boolean
|
||||
*/
|
||||
public function setEx( $key, $ttl, $value )
|
||||
@@ -306,7 +340,7 @@ class _Redis
|
||||
* Sort the elements in a list, set or sorted set.
|
||||
* Overloaded here so we can adjust the key
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $key Key
|
||||
* @param array $options Options: array(key => value, ...) - optional
|
||||
* @return array
|
||||
*/
|
||||
@@ -331,7 +365,7 @@ class _Redis
|
||||
* Returns the whole hash, as an array of strings indexed by strings.
|
||||
* Overloaded here so it can be logged
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $key Key
|
||||
* @return array
|
||||
*/
|
||||
public function hGetAll( $key )
|
||||
@@ -351,9 +385,9 @@ class _Redis
|
||||
* Increments the score of a member from a sorted set by a given amount.
|
||||
* Overloaded here so it can be logged and a ttl set
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $key Key
|
||||
* @param int $inc Value to increment
|
||||
* @param string $value
|
||||
* @param string $value Value
|
||||
* @param int|NULL $ttl TTL in seconds
|
||||
* @return boolean
|
||||
*/
|
||||
@@ -373,7 +407,7 @@ class _Redis
|
||||
/**
|
||||
* Strip prefixes from keys as PHP redis will handle this
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $key Key
|
||||
* @return string
|
||||
*/
|
||||
protected function key( $key )
|
||||
@@ -388,32 +422,6 @@ class _Redis
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cached encryption key
|
||||
*/
|
||||
protected $encryptionKey = NULL;
|
||||
|
||||
/**
|
||||
* Encryption key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _encryptionKey()
|
||||
{
|
||||
if( $this->encryptionKey !== NULL )
|
||||
{
|
||||
return $this->encryptionKey;
|
||||
}
|
||||
|
||||
$this->encryptionKey = \IPS\Settings::i()->sql_pass;
|
||||
if ( function_exists( 'openssl_digest' ) and in_array( 'sha256', openssl_get_md_methods() ) )
|
||||
{
|
||||
$this->encryptionKey = openssl_digest( $this->encryptionKey, 'sha256', TRUE );
|
||||
}
|
||||
|
||||
return $this->encryptionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode
|
||||
@@ -423,15 +431,7 @@ class _Redis
|
||||
*/
|
||||
public function encode( $value )
|
||||
{
|
||||
$value = json_encode( $value );
|
||||
|
||||
if ( $this->canEncrypt )
|
||||
{
|
||||
$iv = \IPS\Login::generateRandomString( 16 );
|
||||
$value = $iv . openssl_encrypt( $value, 'aes-256-ctr', $this->_encryptionKey(), OPENSSL_RAW_DATA, $iv );
|
||||
}
|
||||
|
||||
return $value;
|
||||
return \IPS\Text\Encrypt::fromPlaintext( json_encode( $value ) )->tag();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -442,43 +442,13 @@ class _Redis
|
||||
*/
|
||||
public function decode( $value )
|
||||
{
|
||||
if ( $this->canEncrypt )
|
||||
{
|
||||
$value = @openssl_decrypt( \substr( $value, 16 ), 'aes-256-ctr', $this->_encryptionKey(), OPENSSL_RAW_DATA, \substr( $value, 0, 16 ) );
|
||||
}
|
||||
|
||||
return json_decode( $value, TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a page hit
|
||||
*
|
||||
* @param \IPS\Http\Url|NULL $url URL to log (or null)
|
||||
* @return void
|
||||
*/
|
||||
public function logPageHit( $url=NULL )
|
||||
{
|
||||
$url = $url ? $url : \IPS\Request::i()->url();
|
||||
$key = 'counter-member';
|
||||
|
||||
if ( ! \IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
$key = \IPS\Session::i()->userAgent->spider ? 'counter-spider' : 'counter-guest';
|
||||
}
|
||||
|
||||
$value = $this->connection('write')->hIncrBy( $this->key( $key ), ( \IPS\Session::i()->userAgent->spider ? '/' : ( $url->getFurlQuery() ?: '/' ) ), 1 );
|
||||
$this->connection('write')->expire( $this->key( $key ), static::$ttl );
|
||||
|
||||
if ( \IPS\REDIS_LOG )
|
||||
{
|
||||
static::$log[ sprintf( '%.4f', microtime(true) ) ] = array( 'redis', "Page hit logged " . $key . " = " . ($url->getFurlQuery() ?: '/'), json_encode( $value ) );
|
||||
}
|
||||
return json_decode( \IPS\Text\Encrypt::fromTag( $value )->decrypt(), TRUE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset connection
|
||||
*
|
||||
* @param \RedisException|NULL If this was called as a result of an exception, log that to the debug log
|
||||
* @param \RedisException|NULL $e If this was called as a result of an exception, log that to the debug log
|
||||
* @return void
|
||||
*/
|
||||
public function resetConnection( \RedisException $e = NULL )
|
||||
@@ -497,11 +467,23 @@ class _Redis
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug method to fetch all keys. Never use in production!
|
||||
* Is Redis working?
|
||||
*
|
||||
* @param string $pattern
|
||||
* @param boolean $keyNamesOnly
|
||||
* @return array
|
||||
* @return bool
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
return ( \count( static::$connections ) ) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug method to fetch all keys.
|
||||
*
|
||||
* @warning Never use this in production, as it can expose sensitive data!
|
||||
* @param string $pattern Pattern (* to fetch all)
|
||||
* @param boolean $keyNamesOnly Return names only
|
||||
* @return array
|
||||
* @throws \RedisException
|
||||
*/
|
||||
public function debugGetKeys( $pattern='*', $keyNamesOnly=FALSE )
|
||||
{
|
||||
@@ -526,7 +508,14 @@ class _Redis
|
||||
switch( $type )
|
||||
{
|
||||
case \Redis::REDIS_STRING:
|
||||
$return[ $key . ' (TTL: ' . $ttl . ')' ] = \IPS\Redis::i()->decode( $this->connection('write')->get( $key ) );
|
||||
if ( mb_stristr( $key, '_pg__page_' ) )
|
||||
{
|
||||
$return[ $key . ' (TTL: ' . $ttl . ')' ] = @gzdecode( \IPS\Text\Encrypt::fromCipher( \IPS\Redis::i()->get( $key ) )->decrypt() );
|
||||
}
|
||||
else
|
||||
{
|
||||
$return[ $key . ' (TTL: ' . $ttl . ')' ] = \IPS\Redis::i()->decode( $this->connection('write')->get( $key ) );
|
||||
}
|
||||
break;
|
||||
case \Redis::REDIS_ZSET:
|
||||
$return[ $key . ' (TTL: ' . $ttl . ')' ] = $this->connection('write')->zRange( $key, 0, -1, TRUE );
|
||||
|
||||
Reference in new issue
Block a user