diff --git a/administrator/components/com_config/model/form/application.xml b/administrator/components/com_config/model/form/application.xml index 4e81d2b14e171..b8b467d13746d 100644 --- a/administrator/components/com_config/model/form/application.xml +++ b/administrator/components/com_config/model/form/application.xml @@ -81,6 +81,63 @@ size="5" /> +
+ + + + + + + + + + + + +
+
diff --git a/administrator/components/com_config/view/application/tmpl/default_cache.php b/administrator/components/com_config/view/application/tmpl/default_cache.php index a1256dd98806d..d7a5ce3287dc4 100644 --- a/administrator/components/com_config/view/application/tmpl/default_cache.php +++ b/administrator/components/com_config/view/application/tmpl/default_cache.php @@ -20,4 +20,10 @@ { $this->fieldsname .= ',memcache'; } +if (isset($this->data['cache_handler']) + || $this->data['cache_handler'] == 'redis' +) +{ + $this->fieldsname .= ',redis'; +} echo JLayoutHelper::render('joomla.content.options_default', $this); diff --git a/administrator/language/en-GB/en-GB.com_config.ini b/administrator/language/en-GB/en-GB.com_config.ini index e73191517a92b..a8a5cf2f22e15 100644 --- a/administrator/language/en-GB/en-GB.com_config.ini +++ b/administrator/language/en-GB/en-GB.com_config.ini @@ -127,6 +127,16 @@ COM_CONFIG_FIELD_MEMCACHE_PERSISTENT_DESC="Persistent Memcache(d)" COM_CONFIG_FIELD_MEMCACHE_PERSISTENT_LABEL="Persistent Memcache(d)" COM_CONFIG_FIELD_MEMCACHE_PORT_DESC="Memcache(d) Server Port" COM_CONFIG_FIELD_MEMCACHE_PORT_LABEL="Memcache(d) Server Port" +COM_CONFIG_FIELD_REDIS_AUTH_DESC="Redis Server Authentication" +COM_CONFIG_FIELD_REDIS_AUTH_LABEL="Redis Server Authentication" +COM_CONFIG_FIELD_REDIS_DB_DESC="Redis Database" +COM_CONFIG_FIELD_REDIS_DB_LABEL="Redis Database" +COM_CONFIG_FIELD_REDIS_HOST_DESC="Redis Server Host" +COM_CONFIG_FIELD_REDIS_HOST_LABEL="Redis Server Host" +COM_CONFIG_FIELD_REDIS_PERSISTENT_DESC="Persistent Redis" +COM_CONFIG_FIELD_REDIS_PERSISTENT_LABEL="Persistent Redis" +COM_CONFIG_FIELD_REDIS_PORT_DESC="Redis Server Port" +COM_CONFIG_FIELD_REDIS_PORT_LABEL="Redis Server Port" COM_CONFIG_FIELD_METAAUTHOR_DESC="Show the author meta tag when viewing articles" COM_CONFIG_FIELD_METAAUTHOR_LABEL="Show Author Meta Tag" COM_CONFIG_FIELD_METADESC_DESC="Enter a description of the overall Web site that is to be used by search engines. Generally, a maximum of 20 words is optimal." diff --git a/administrator/language/en-GB/en-GB.lib_joomla.ini b/administrator/language/en-GB/en-GB.lib_joomla.ini index a0b73be1294e9..eff63d6bbb33b 100644 --- a/administrator/language/en-GB/en-GB.lib_joomla.ini +++ b/administrator/language/en-GB/en-GB.lib_joomla.ini @@ -305,6 +305,7 @@ JLIB_FORM_VALUE_CACHE_EACCELERATOR="eAccelerator" JLIB_FORM_VALUE_CACHE_FILE="File" JLIB_FORM_VALUE_CACHE_MEMCACHE="Memcache" JLIB_FORM_VALUE_CACHE_MEMCACHED="Memcached (Experimental)" +JLIB_FORM_VALUE_CACHE_REDIS="Redis" JLIB_FORM_VALUE_CACHE_WINCACHE="Windows Cache" JLIB_FORM_VALUE_CACHE_XCACHE="XCache" JLIB_FORM_VALUE_SESSION_APC="Alternative PHP Cache" diff --git a/libraries/joomla/cache/storage/redis.php b/libraries/joomla/cache/storage/redis.php new file mode 100644 index 0000000000000..3cdbff09bc2dc --- /dev/null +++ b/libraries/joomla/cache/storage/redis.php @@ -0,0 +1,359 @@ +getConnection(); + } + } + + /** + * Return redis connection object + * + * @return object redis connection object + * + * @since 13.1 + * @throws RuntimeException + */ + protected function getConnection() + { + if (static::isSupported() == false) + { + return false; + } + + $config = JFactory::getConfig(); + $app = JFactory::getApplication(); + + $caching = (bool)$config->get('caching'); + if ($caching == false) + { + return false; + } + + $this->_persistent = $config->get('redis_persist', true); + + $server = array(); + $server['host'] = $config->get('redis_server_host', 'localhost'); + $server['port'] = $config->get('redis_server_port', 6379); + $server['auth'] = $config->get('redis_server_auth', NULL); + $server['db'] = (int)$config->get('redis_server_db', NULL); + + self::$_redis = new Redis(); + if($this->_persistent) + { + try + { + $connection = self::$_redis->pconnect($server['host'], $server['port']); + $auth = (!empty($server['auth'])) ? self::$_redis->auth($server['auth']) : true; + } + + catch(Exception $e) + { + + } + } + else + { + try + { + $connection = self::$_redis->connect($server['host'], $server['port']); + $auth = (!empty($server['auth'])) ? self::$_redis->auth($server['auth']) : true; + } + + catch(Exception $e) + { + + } + } + + if ($connection == false) + { + self::$_redis = null; + if ($app->isAdmin()) + { + JError::raiseWarning(500, 'Redis connection failed'); + } + + return; + } + + if ($auth == false) + { + if ($app->isAdmin()) + { + JError::raiseWarning(500, 'Redis authentication failed'); + } + + return; + } + + $select = self::$_redis->select($server['db']); + if($select == false) + { + self::$_redis = null; + if ($app->isAdmin()) + { + JError::raiseWarning(500, 'Redis failed to select database'); + } + + return; + } + + try + { + self::$_redis->ping(); + } + + catch(RedisException $e) + { + self::$_redis = null; + if ($app->isAdmin()) + { + JError::raiseWarning(500, 'Redis ping failed'); + } + + return; + } + + return self::$_redis; + } + + /** + * Get cached data from redis by id and group + * + * @param string $id The cache data id + * @param string $group The cache data group + * @param boolean $checkTime True to verify cache time expiration threshold + * + * @return mixed Boolean false on failure or a cached data string + * + * @since 13.1 + */ + public function get($id, $group, $checkTime = true) + { + if (self::isConnected() == false) + { + return false; + } + + $cache_id = $this->_getCacheId($id, $group); + $back = self::$_redis->get($cache_id); + + return $back; + } + + /** + * Get all cached data + * + * @return array data + * + * @since 13.1 + */ + public function getAll() + { + if (self::isConnected() == false) + { + return false; + } + + parent::getAll(); + + $allKeys = self::$_redis->keys('*'); + $data = array(); + $secret = $this->_hash; + + if (!empty($allKeys)) + { + foreach ($allKeys as $key) + { + $namearr = explode('-', $key); + + if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache') + { + $group = $namearr[2]; + + if (!isset($data[$group])){ + $item = new JCacheStorageHelper($group); + } + else + { + $item = $data[$group]; + } + $item->updateSize(strlen($key)*8/1024); + $data[$group] = $item; + } + } + } + + return $data; + } + + /** + * Store the data to redis by id and group + * + * @param string $id The cache data id + * @param string $group The cache data group + * @param string $data The data to store in cache + * + * @return boolean True on success, false otherwise + * + * @since 13.1 + */ + public function store($id, $group, $data) + { + if (self::isConnected() == false) + { + return false; + } + + $cache_id = $this->_getCacheId($id, $group); + + $tmparr = new stdClass; + $tmparr->name = $cache_id; + $tmparr->size = strlen($data); + + $config = JFactory::getConfig(); + $lifetime = (int) $config->get('cachetime', 15); + if ($this->_lifetime == $lifetime) + { + $this->_lifetime = $lifetime * 60; + } + + $index[] = $tmparr; + + self::$_redis->setex($cache_id, 3600, $data); + + return true; + } + + /** + * Remove a cached data entry by id and group + * + * @param string $id The cache data id + * @param string $group The cache data group + * + * @return boolean True on success, false otherwise + * + * @since 13.1 + */ + public function remove($id, $group) + { + if (self::isConnected() == false) + { + return false; + } + + $cache_id = $this->_getCacheId($id, $group); + return self::$_redis->delete($cache_id); + } + + /** + * Clean cache for a group given a mode. + * + * @param string $group The cache data group + * @param string $mode The mode for cleaning cache [group|notgroup] + * group mode : cleans all cache in the group + * notgroup mode : cleans all cache not in the group + * + * @return boolean True on success, false otherwise + * + * @since 13.1 + */ + public function clean($group, $mode = null) + { + if (self::isConnected() == false) + { + return false; + } + + $allKeys = self::$_redis->keys('*'); + if ($allKeys === false) + { + $allKeys = array(); + } + $secret = $this->_hash; + + foreach ($allKeys as $key) + { + if (strpos($key, $secret . '-cache-' . $group . '-') === 0 && $mode == 'group') + { + self::$_redis->delete($key); + } + if (strpos($key, $secret . '-cache-' . $group . '-') !== 0 && $mode != 'group') + { + self::$_redis->delete($key); + } + } + + return true; + } + + /** + * Test to see if the cache storage is available. + * + * @return boolean True on success, false otherwise. + * + * @since 13.1 + */ + public static function isSupported() + { + return class_exists('Redis'); + } + + /** + * Test to see if Redis connection is up + * + * @return boolean True on success, false otherwise. + * + * @since 13.1 + */ + public static function isConnected() + { + return (bool)self::$_redis; + } +}