Carsten Brandt
11 years ago
27 changed files with 2554 additions and 2445 deletions
@ -0,0 +1,184 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\redis; |
||||
|
||||
/** |
||||
* Redis Cache implements a cache application component based on [redis](http://redis.io/) key-value store. |
||||
* |
||||
* Redis Cache requires redis version 2.6.12 or higher to work properly. |
||||
* |
||||
* It needs to be configured with a redis [[Connection]] that is also configured as an application component. |
||||
* By default it will use the `redis` application component. |
||||
* |
||||
* See [[Cache]] manual for common cache operations that redis Cache supports. |
||||
* |
||||
* Unlike the [[Cache]], redis Cache allows the expire parameter of [[set]], [[add]], [[mset]] and [[madd]] to |
||||
* be a floating point number, so you may specify the time in milliseconds (e.g. 0.1 will be 100 milliseconds). |
||||
* |
||||
* To use redis Cache as the cache application component, configure the application as follows, |
||||
* |
||||
* ~~~ |
||||
* [ |
||||
* 'components' => [ |
||||
* 'cache' => [ |
||||
* 'class' => 'yii\redis\Cache', |
||||
* ], |
||||
* 'redis' => [ |
||||
* 'class' => 'yii\redis\Connection', |
||||
* 'hostname' => 'localhost', |
||||
* 'port' => 6379, |
||||
* 'database' => 0, |
||||
* ] |
||||
* ], |
||||
* ] |
||||
* ~~~ |
||||
* |
||||
* @property Connection $connection The redis connection object. This property is read-only. |
||||
* |
||||
* @author Carsten Brandt <mail@cebe.cc> |
||||
* @since 2.0 |
||||
*/ |
||||
class Cache extends \yii\caching\Cache |
||||
{ |
||||
/** |
||||
* @var string the id of the application component to use as the redis connection. |
||||
* It should be configured as a [[yii\redis\Connection]]. Defaults to `redis`. |
||||
*/ |
||||
public $connectionId = 'redis'; |
||||
|
||||
|
||||
/** |
||||
* Checks whether a specified key exists in the cache. |
||||
* This can be faster than getting the value from the cache if the data is big. |
||||
* Note that this method does not check whether the dependency associated |
||||
* with the cached data, if there is any, has changed. So a call to [[get]] |
||||
* may return false while exists returns true. |
||||
* @param mixed $key a key identifying the cached value. This can be a simple string or |
||||
* a complex data structure consisting of factors representing the key. |
||||
* @return boolean true if a value exists in cache, false if the value is not in the cache or expired. |
||||
*/ |
||||
public function exists($key) |
||||
{ |
||||
/** @var Connection $connection */ |
||||
$connection = \Yii::$app->getComponent($this->connectionId); |
||||
return (bool) $connection->executeCommand('EXISTS', [$this->buildKey($key)]); |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function getValue($key) |
||||
{ |
||||
/** @var Connection $connection */ |
||||
$connection = \Yii::$app->getComponent($this->connectionId); |
||||
return $connection->executeCommand('GET', [$key]); |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function getValues($keys) |
||||
{ |
||||
/** @var Connection $connection */ |
||||
$connection = \Yii::$app->getComponent($this->connectionId); |
||||
$response = $connection->executeCommand('MGET', $keys); |
||||
$result = []; |
||||
$i = 0; |
||||
foreach ($keys as $key) { |
||||
$result[$key] = $response[$i++]; |
||||
} |
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function setValue($key, $value, $expire) |
||||
{ |
||||
/** @var Connection $connection */ |
||||
$connection = \Yii::$app->getComponent($this->connectionId); |
||||
if ($expire == 0) { |
||||
return (bool) $connection->executeCommand('SET', [$key, $value]); |
||||
} else { |
||||
$expire = (int) ($expire * 1000); |
||||
return (bool) $connection->executeCommand('SET', [$key, $value, 'PX', $expire]); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function setValues($data, $expire) |
||||
{ |
||||
/** @var Connection $connection */ |
||||
$connection = \Yii::$app->getComponent($this->connectionId); |
||||
|
||||
$args = []; |
||||
foreach($data as $key => $value) { |
||||
$args[] = $key; |
||||
$args[] = $value; |
||||
} |
||||
|
||||
$failedKeys = []; |
||||
if ($expire == 0) { |
||||
$connection->executeCommand('MSET', $args); |
||||
} else { |
||||
$expire = (int) ($expire * 1000); |
||||
$connection->executeCommand('MULTI'); |
||||
$connection->executeCommand('MSET', $args); |
||||
$index = []; |
||||
foreach ($data as $key => $value) { |
||||
$connection->executeCommand('PEXPIRE', [$key, $expire]); |
||||
$index[] = $key; |
||||
} |
||||
$result = $connection->executeCommand('EXEC'); |
||||
array_shift($result); |
||||
foreach($result as $i => $r) { |
||||
if ($r != 1) { |
||||
$failedKeys[] = $index[$i]; |
||||
} |
||||
} |
||||
} |
||||
return $failedKeys; |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function addValue($key, $value, $expire) |
||||
{ |
||||
/** @var Connection $connection */ |
||||
$connection = \Yii::$app->getComponent($this->connectionId); |
||||
if ($expire == 0) { |
||||
return (bool) $connection->executeCommand('SET', [$key, $value, 'NX']); |
||||
} else { |
||||
$expire = (int) ($expire * 1000); |
||||
return (bool) $connection->executeCommand('SET', [$key, $value, 'PX', $expire, 'NX']); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function deleteValue($key) |
||||
{ |
||||
/** @var Connection $connection */ |
||||
$connection = \Yii::$app->getComponent($this->connectionId); |
||||
return (bool) $connection->executeCommand('DEL', [$key]); |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function flushValues() |
||||
{ |
||||
/** @var Connection $connection */ |
||||
$connection = \Yii::$app->getComponent($this->connectionId); |
||||
return $connection->executeCommand('FLUSHDB'); |
||||
} |
||||
} |
@ -0,0 +1,32 @@
|
||||
The Yii framework is free software. It is released under the terms of |
||||
the following BSD License. |
||||
|
||||
Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com) |
||||
All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions |
||||
are met: |
||||
|
||||
* Redistributions of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above copyright |
||||
notice, this list of conditions and the following disclaimer in |
||||
the documentation and/or other materials provided with the |
||||
distribution. |
||||
* Neither the name of Yii Software LLC nor the names of its |
||||
contributors may be used to endorse or promote products derived |
||||
from this software without specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,91 @@
|
||||
Redis Cache and ActiveRecord for Yii 2 |
||||
====================================== |
||||
|
||||
This extension provides the [redis](http://redis.io/) key-value store support for the Yii2 framework. |
||||
It includes a `Cache` class and implents the `ActiveRecord` pattern that allows you to store active |
||||
records in redis. |
||||
|
||||
To use this extension, you have to configure the Connection class in your application configuration: |
||||
|
||||
```php |
||||
return [ |
||||
//.... |
||||
'components' => [ |
||||
'redis' => [ |
||||
'class' => 'yii\redis\Connection', |
||||
'hostname' => 'localhost', |
||||
'port' => 6379, |
||||
'database' => 0, |
||||
], |
||||
] |
||||
]; |
||||
``` |
||||
|
||||
To use the `Cache` component, you also have to configure the `cache` component to be `yii\redis\Cache`: |
||||
|
||||
```php |
||||
return [ |
||||
//.... |
||||
'components' => [ |
||||
// ... |
||||
'cache' => [ |
||||
'class' => 'yii\redis\Cache', |
||||
], |
||||
] |
||||
]; |
||||
``` |
||||
|
||||
|
||||
Installation |
||||
------------ |
||||
|
||||
The preferred way to install this extension is through [composer](http://getcomposer.org/download/). |
||||
|
||||
Either run |
||||
|
||||
``` |
||||
php composer.phar require yiisoft/yii2-redis "*" |
||||
``` |
||||
|
||||
or add |
||||
|
||||
```json |
||||
"yiisoft/yii2-redis": "*" |
||||
``` |
||||
|
||||
to the require section of your composer.json. |
||||
|
||||
|
||||
Using the redis ActiveRecord |
||||
---------------------------- |
||||
|
||||
For general information on how to use yii's ActiveRecord please refer to the [guide](https://github.com/yiisoft/yii2/blob/master/docs/guide/active-record.md). |
||||
|
||||
For defining a redis ActiveRecord class your record class needs to extend from `yii\redis\ActiveRecord` and |
||||
implement at least the `attributes()` method to define the attributes of the record. |
||||
A primary key can be defined via [[primaryKey()]] which defaults to `id` if not specified. |
||||
The primaryKey needs to be part of the attributes so make sure you have an `id` attribute defined if you do |
||||
not specify your own primary key. |
||||
|
||||
The following is an example model called `Customer`: |
||||
|
||||
```php |
||||
class Customer extends \yii\redis\ActiveRecord |
||||
{ |
||||
public function attributes() |
||||
{ |
||||
return ['id', 'name', 'address', 'registration_date']; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
The general usage of redis ActiveRecord is very similar to the database ActiveRecord as described in the |
||||
[guide](https://github.com/yiisoft/yii2/blob/master/docs/guide/active-record.md). |
||||
It supports the same interface and features except the following limitations: |
||||
|
||||
- As redis does not support SQL the query API is limited to the following methods: |
||||
`where()`, `limit()`, `offset()`, `orderBy()` and `indexBy()`. |
||||
(orderBy() is not yet implemented: [#1305](https://github.com/yiisoft/yii2/issues/1305)) |
||||
- `via`-relations can not be defined via a table as there are not tables in redis. You can only define relations via other records. |
||||
|
||||
It is also possible to define relations from redis ActiveRecords to normal ActiveRecord classes and vice versa. |
@ -0,0 +1,27 @@
|
||||
{ |
||||
"name": "yiisoft/yii2-redis", |
||||
"description": "Redis Cache and ActiveRecord for the Yii framework", |
||||
"keywords": ["yii", "redis", "active-record", "cache"], |
||||
"type": "yii2-extension", |
||||
"license": "BSD-3-Clause", |
||||
"support": { |
||||
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Aredis", |
||||
"forum": "http://www.yiiframework.com/forum/", |
||||
"wiki": "http://www.yiiframework.com/wiki/", |
||||
"irc": "irc://irc.freenode.net/yii", |
||||
"source": "https://github.com/yiisoft/yii2" |
||||
}, |
||||
"authors": [ |
||||
{ |
||||
"name": "Carsten Brandt", |
||||
"email": "mail@cebe.cc" |
||||
} |
||||
], |
||||
"require": { |
||||
"yiisoft/yii2": "*" |
||||
}, |
||||
"autoload": { |
||||
"psr-0": { "yii\\redis\\": "" } |
||||
}, |
||||
"target-dir": "yii/redis" |
||||
} |
@ -1,218 +0,0 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\caching; |
||||
|
||||
use yii\redis\Connection; |
||||
|
||||
/** |
||||
* RedisCache implements a cache application component based on [redis](http://redis.io/) version 2.6.12 or higher. |
||||
* |
||||
* RedisCache needs to be configured with [[hostname]], [[port]] and [[database]] of the server |
||||
* to connect to. By default RedisCache assumes there is a redis server running on localhost at |
||||
* port 6379 and uses the database number 0. |
||||
* |
||||
* RedisCache also supports [the AUTH command](http://redis.io/commands/auth) of redis. |
||||
* When the server needs authentication, you can set the [[password]] property to |
||||
* authenticate with the server after connect. |
||||
* |
||||
* See [[Cache]] manual for common cache operations that RedisCache supports. |
||||
* Unlike the [[CCache]], RedisCache allows the expire parameter of |
||||
* [[set]] and [[add]] to be a floating point number, so you may specify the time in milliseconds. |
||||
* |
||||
* To use RedisCache as the cache application component, configure the application as follows, |
||||
* |
||||
* ~~~ |
||||
* [ |
||||
* 'components' => [ |
||||
* 'cache' => [ |
||||
* 'class' => 'RedisCache', |
||||
* 'hostname' => 'localhost', |
||||
* 'port' => 6379, |
||||
* 'database' => 0, |
||||
* ], |
||||
* ], |
||||
* ] |
||||
* ~~~ |
||||
* |
||||
* @property Connection $connection The redis connection object. This property is read-only. |
||||
* |
||||
* @author Carsten Brandt <mail@cebe.cc> |
||||
* @since 2.0 |
||||
*/ |
||||
class RedisCache extends Cache |
||||
{ |
||||
/** |
||||
* @var string hostname to use for connecting to the redis server. Defaults to 'localhost'. |
||||
*/ |
||||
public $hostname = 'localhost'; |
||||
/** |
||||
* @var int the port to use for connecting to the redis server. Default port is 6379. |
||||
*/ |
||||
public $port = 6379; |
||||
/** |
||||
* @var string the password to use to authenticate with the redis server. If not set, no AUTH command will be sent. |
||||
*/ |
||||
public $password; |
||||
/** |
||||
* @var int the redis database to use. This is an integer value starting from 0. Defaults to 0. |
||||
*/ |
||||
public $database = 0; |
||||
/** |
||||
* @var float timeout to use for connection to redis. If not set the timeout set in php.ini will be used: ini_get("default_socket_timeout") |
||||
*/ |
||||
public $connectionTimeout = null; |
||||
/** |
||||
* @var float timeout to use for redis socket when reading and writing data. If not set the php default value will be used. |
||||
*/ |
||||
public $dataTimeout = null; |
||||
/** |
||||
* @var Connection the redis connection |
||||
*/ |
||||
private $_connection; |
||||
|
||||
|
||||
/** |
||||
* Initializes the cache component by establishing a connection to the redis server. |
||||
*/ |
||||
public function init() |
||||
{ |
||||
parent::init(); |
||||
$this->getConnection(); |
||||
} |
||||
|
||||
/** |
||||
* Returns the redis connection object. |
||||
* Establishes a connection to the redis server if it does not already exists. |
||||
* @return Connection the redis connection object. |
||||
*/ |
||||
public function getConnection() |
||||
{ |
||||
if ($this->_connection === null) { |
||||
$this->_connection = new Connection([ |
||||
'dsn' => 'redis://' . $this->hostname . ':' . $this->port . '/' . $this->database, |
||||
'password' => $this->password, |
||||
'connectionTimeout' => $this->connectionTimeout, |
||||
'dataTimeout' => $this->dataTimeout, |
||||
]); |
||||
} |
||||
return $this->_connection; |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a specified key exists in the cache. |
||||
* This can be faster than getting the value from the cache if the data is big. |
||||
* Note that this method does not check whether the dependency associated |
||||
* with the cached data, if there is any, has changed. So a call to [[get]] |
||||
* may return false while exists returns true. |
||||
* @param mixed $key a key identifying the cached value. This can be a simple string or |
||||
* a complex data structure consisting of factors representing the key. |
||||
* @return boolean true if a value exists in cache, false if the value is not in the cache or expired. |
||||
*/ |
||||
public function exists($key) |
||||
{ |
||||
return (bool) $this->_connection->executeCommand('EXISTS', [$this->buildKey($key)]); |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function getValue($key) |
||||
{ |
||||
return $this->_connection->executeCommand('GET', [$key]); |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function getValues($keys) |
||||
{ |
||||
$response = $this->_connection->executeCommand('MGET', $keys); |
||||
$result = []; |
||||
$i = 0; |
||||
foreach ($keys as $key) { |
||||
$result[$key] = $response[$i++]; |
||||
} |
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function setValue($key, $value, $expire) |
||||
{ |
||||
if ($expire == 0) { |
||||
return (bool) $this->_connection->executeCommand('SET', [$key, $value]); |
||||
} else { |
||||
$expire = (int) ($expire * 1000); |
||||
return (bool) $this->_connection->executeCommand('SET', [$key, $value, 'PX', $expire]); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function setValues($data, $expire) |
||||
{ |
||||
$args = []; |
||||
foreach($data as $key => $value) { |
||||
$args[] = $key; |
||||
$args[] = $value; |
||||
} |
||||
|
||||
$failedKeys = []; |
||||
if ($expire == 0) { |
||||
$this->_connection->executeCommand('MSET', $args); |
||||
} else { |
||||
$expire = (int) ($expire * 1000); |
||||
$this->_connection->executeCommand('MULTI'); |
||||
$this->_connection->executeCommand('MSET', $args); |
||||
$index = []; |
||||
foreach ($data as $key => $value) { |
||||
$this->_connection->executeCommand('PEXPIRE', [$key, $expire]); |
||||
$index[] = $key; |
||||
} |
||||
$result = $this->_connection->executeCommand('EXEC'); |
||||
array_shift($result); |
||||
foreach($result as $i => $r) { |
||||
if ($r != 1) { |
||||
$failedKeys[] = $index[$i]; |
||||
} |
||||
} |
||||
} |
||||
return $failedKeys; |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function addValue($key, $value, $expire) |
||||
{ |
||||
if ($expire == 0) { |
||||
return (bool) $this->_connection->executeCommand('SET', [$key, $value, 'NX']); |
||||
} else { |
||||
$expire = (int) ($expire * 1000); |
||||
return (bool) $this->_connection->executeCommand('SET', [$key, $value, 'PX', $expire, 'NX']); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function deleteValue($key) |
||||
{ |
||||
return (bool) $this->_connection->executeCommand('DEL', [$key]); |
||||
} |
||||
|
||||
/** |
||||
* @inheritDocs |
||||
*/ |
||||
protected function flushValues() |
||||
{ |
||||
return $this->_connection->executeCommand('FLUSHDB'); |
||||
} |
||||
} |
@ -1,8 +1,7 @@
|
||||
<?php |
||||
|
||||
namespace yiiunit\framework\redis; |
||||
namespace yiiunit\extensions\redis; |
||||
|
||||
use yii\db\Query; |
||||
use yii\redis\ActiveQuery; |
||||
use yiiunit\data\ar\redis\ActiveRecord; |
||||
use yiiunit\data\ar\redis\Customer; |
Loading…
Reference in new issue