Invision Power Services, Inc. * @copyright (c) Invision Power Services, Inc. * @license https://www.invisioncommunity.com/legal/standards/ * @package Invision Community * @since 07 May 2013 */ namespace IPS\Data; /* 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; } /** * Abstract Storage Class */ abstract class _Store extends AbstractData { /** * @brief Instance */ protected static $instance; /** * Get instance * * @return \IPS\Data\Store */ public static function i() { if( static::$instance === NULL ) { $classname = 'IPS\Data\Store\\' . \IPS\STORE_METHOD; static::$instance = new $classname( json_decode( \IPS\STORE_CONFIG, TRUE ) ); } return static::$instance; } /** * @brief Always needed Store keys */ public $initLoad = array(); /** * @brief Template store keys */ public $templateLoad = array(); /** * @brief Log */ 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 * * @param array $keys Keys * @return void */ public function loadIntoMemory( array $keys ) { } /** * Test the datastore engine to make sure it's working * * @return bool */ public function test() { /* Set a test key */ $key = 'test_' . md5( uniqid() ); $this->$key = '1'; /* Now clear our internal cache to ensure the value isn't returned from there */ unset( $this->_data[ $key ], $this->_exists[ $key ], static::$cache[ $key ] ); /* And then try to retrieve it */ if( isset( $this->$key ) ) { $value = $this->$key; unset( $this->$key ); return ( $value == 1 ) ? TRUE : FALSE; } return FALSE; } /** * Magic Method: Get * * @param string $key Key * @return string Value from the _datastore */ public function __get( $key ) { /* Try to get it from the cache store... */ 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->cacheCheck === false ) { if ( isset( \IPS\Data\Cache::i()->$key ) and \IPS\Data\Cache::i()->$key !== NULL ) { /* We accept it may be stale, so return it */ return \IPS\Data\Cache::i()->$key; } /* Still here? throw an exception so we get it from the data storage engine */ throw new \OutOfRangeException; } else { /* 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 ) ) { /* 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 ) ) ) ) { return $value; } } } } /* Still here? throw an exception so we get it from the data storage engine */ throw new \OutOfRangeException; } /* If we couldn't get it from the cache engine, get it from the data storage engine */ catch ( \OutOfRangeException $e ) { /* Actually get it... */ $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' ) { /* 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; } return $value; } } /** * Magic Method: Set * * @param string $key Key * @param string $value Value * @return void */ public function __set( $key, $value ) { /* Actually set it in the data storage engine */ 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' ) { /* 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 ) ) ) { $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 ) ) ); } } } /** * Magic Method: Isset * * @param string $key Key * @return bool */ 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' ) { /* Does it exist in the caching engine? */ if ( isset( \IPS\Data\Cache::i()->$key ) ) { /* Get it... */ $value = \IPS\Data\Cache::i()->$key; if ( $this->cacheCheck === false ) { /* We accept it may be stale, 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 ) ) ) ) { return TRUE; } } } /* If we're still here, check the data storage engine... */ return parent::__isset( $key ); } /** * Magic Method: Unset * * @param string $key Key * @return void */ public function __unset( $key ) { /* Unset it in the data storage engine */ 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' ) { /* 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; } } }