Browse Source

Redis connection implementation

tags/2.0.0-beta
Carsten Brandt 12 years ago
parent
commit
8e74add1e7
  1. 63
      framework/db/redis/Connection.php
  2. 41
      tests/unit/framework/db/redis/ConnectionTest.php
  3. 45
      tests/unit/framework/db/redis/RedisTestCase.php

63
framework/db/redis/Connection.php

@ -60,7 +60,7 @@ class Connection extends Component
public $keyPrefix; public $keyPrefix;
/** /**
* @var array http://redis.io/commands * @var array List of available redis commands http://redis.io/commands
*/ */
public $redisCommands = array( public $redisCommands = array(
'BRPOP', // key [key ...] timeout Remove and get the last element in a list, or block until one is available 'BRPOP', // key [key ...] timeout Remove and get the last element in a list, or block until one is available
@ -268,7 +268,7 @@ class Connection extends Component
{ {
if ($this->_socket !== null) { if ($this->_socket !== null) {
\Yii::trace('Closing DB connection: ' . $this->dsn, __CLASS__); \Yii::trace('Closing DB connection: ' . $this->dsn, __CLASS__);
// TODO send CLOSE to the server $this->__call('CLOSE', array()); // TODO improve API
stream_socket_shutdown($this->_socket, STREAM_SHUT_RDWR); stream_socket_shutdown($this->_socket, STREAM_SHUT_RDWR);
$this->_socket = null; $this->_socket = null;
$this->_transaction = null; $this->_transaction = null;
@ -323,23 +323,70 @@ class Connection extends Component
} }
} }
/**
* http://redis.io/topics/protocol
* https://github.com/ptrofimov/tinyredisclient/blob/master/src/TinyRedisClient.php
*
* @param string $name
* @param array $params
* @return mixed
*/
public function __call($name, $params) public function __call($name, $params)
{ {
// TODO set active to true?
if (in_array($name, $this->redisCommands)) if (in_array($name, $this->redisCommands))
{ {
array_unshift($params, $name); array_unshift($params, $name);
$cmd = '*' . count($params) . "\r\n"; $command = '*' . count($params) . "\r\n";
foreach($params as $arg) { foreach($params as $arg) {
$cmd .= '$' . strlen( $item ) . "\r\n" . $item . "\r\n"; $command .= '$' . strlen($arg) . "\r\n" . $arg . "\r\n";
} }
fwrite( $this->_socket, $cmd ); \Yii::trace("Executing Redis Command: {$command}", __CLASS__);
return $this->_parseResponse(); fwrite($this->_socket, $command);
return $this->parseResponse($command);
} }
else { else {
return parent::__call($name, $params); return parent::__call($name, $params);
} }
} }
private function parseResponse($command)
{
if(($line = fgets($this->_socket))===false) {
throw new Exception("Failed to read from socket.\nRedis command was: " . $command);
}
$type = $line[0];
$line = substr($line, 1, -2);
switch($type)
{
case '+': // Status reply
return true;
case '-': // Error reply
throw new Exception("Redis error: " . $line . "\nRedis command was: " . $command);
case ':': // Integer reply
// no cast to integer as it is in the range of a signed 64 bit integer
return $line;
case '$': // Bulk replies
if ($line == '-1') {
return null;
}
$data = fread($this->_socket, $line + 2);
if($data===false) {
throw new Exception("Failed to read from socket.\nRedis command was: " . $command);
}
return substr($data, 0, -2);
case '*': // Multi-bulk replies
$count = (int) $line;
$data = array();
for($i = 0; $i < $count; $i++) {
$data[] = $this->parseResponse($command);
}
return $data;
default:
throw new Exception('Received illegal data from redis: ' . substr($line, 0, -2) . "\nRedis command was: " . $command);
}
}
/** /**
* Returns the statistical results of SQL queries. * Returns the statistical results of SQL queries.
* The results returned include the number of SQL statements executed and * The results returned include the number of SQL statements executed and
@ -350,9 +397,9 @@ class Connection extends Component
* @see \yii\logging\Logger::getProfiling() * @see \yii\logging\Logger::getProfiling()
*/ */
public function getQuerySummary() public function getQuerySummary()
{// TODO implement {
$logger = \Yii::getLogger(); $logger = \Yii::getLogger();
$timings = $logger->getProfiling(array('yii\db\Command::query', 'yii\db\Command::execute')); $timings = $logger->getProfiling(array('yii\db\redis\Connection::command'));
$count = count($timings); $count = count($timings);
$time = 0; $time = 0;
foreach ($timings as $timing) { foreach ($timings as $timing) {

41
tests/unit/framework/db/redis/ConnectionTest.php

@ -0,0 +1,41 @@
<?php
namespace yiiunit\framework\db\redis;
use yii\db\redis\Connection;
/**
*
*
* @author Carsten Brandt <mail@cebe.cc>
*/
class ConnectionTest extends RedisTestCase
{
public function testConstruct()
{
$db = new Connection();
}
public function storeGetData()
{
return array(
array(123),
array(-123),
array(0),
array('test'),
array("test\r\ntest"),
array(json_encode($this)),
);
}
/**
* @dataProvider storeGetData
*/
public function testStoreGet($data)
{
$db = $this->getConnection(true);
$db->SET('hi', $data);
$this->assertEquals($data, $db->GET('hi'));
}
}

45
tests/unit/framework/db/redis/RedisTestCase.php

@ -0,0 +1,45 @@
<?php
/**
*
*
* @author Carsten Brandt <mail@cebe.cc>
*/
namespace yiiunit\framework\db\redis;
use yii\db\redis\Connection;
use yiiunit\TestCase;
class RedisTestCase extends TestCase
{
function __construct()
{
// TODO check if a redis server is running
//$this->markTestSkipped('No redis server running at port ...');
}
/**
* @param bool $reset whether to clean up the test database
* @return Connection
*/
function getConnection($reset = true)
{
$params = $this->getParam('redis');
$db = new \yii\db\redis\Connection;
$db->dsn = $params['dsn'];
$db->username = $params['username'];
$db->password = $params['password'];
if ($reset) {
// TODO implement
/* $db->open();
$lines = explode(';', file_get_contents($params['fixture']));
foreach ($lines as $line) {
if (trim($line) !== '') {
$db->pdo->exec($line);
}
}*/
}
return $db;
}
}
Loading…
Cancel
Save