Compare commits

...

18 Commits

Author SHA1 Message Date
Daniele Alessandri c546351e1d Backported changes from the mainline library to the PHP 5.2 branch (up to commit 3c33c74) 2010-05-24 19:37:00 +02:00
Daniele Alessandri 6ca4087118 Backported changes from the mainline library to the PHP 5.2 branch (up to commit 0e69438) 2010-05-23 10:04:05 +02:00
Daniele Alessandri 654b11ba6d Backported changes from the mainline library to the PHP 5.2 branch (up to commit c781e91) 2010-05-22 17:26:01 +02:00
Daniele Alessandri cf758f23d4 Backported changes from the mainline library to the PHP 5.2 branch (up to commit 84cb28e) 2010-05-18 20:28:54 +02:00
Daniele Alessandri 7806614041 Backported changes from the mainline library to the PHP 5.2 branch (up to commit 35f7ea2) 2010-05-16 13:05:56 +02:00
Daniele Alessandri 852959cbd1 Backported changes from the mainline library to the PHP 5.2 branch (up to commit 3edcdc5) 2010-01-28 00:00:01 +01:00
Daniele Alessandri 3ebdf16d1c Backported changes from the mainline library to the PHP 5.2 branch (up to commit c81e02b) 2010-01-23 16:51:07 +01:00
Daniele Alessandri 35142cfbcf Backported changes from the mainline library to the PHP 5.2 branch (up to commit 35016ba) 2010-01-09 14:54:20 +01:00
Daniele Alessandri 0f1d4675cf Backported the test suite to PHP 5.2. This was the last blocking point for a full PHP 5.2-compatible release of Predis." 2009-12-31 17:32:24 +01:00
Daniele Alessandri 465bd45147 Backported changes from the mainline library to the PHP 5.2 branch (up to commit 2a06d84) 2009-12-31 15:21:48 +01:00
Daniele Alessandri 3bfe3897a7 Backported changes from the mainline library to the PHP 5.2 branch (up to commit b757afe) 2009-12-27 11:06:30 +01:00
Daniele Alessandri 0f966af8e3 Backported changes from the mainline library to the PHP 5.2 branch (up to commit 6814845) 2009-12-26 19:48:55 +01:00
Daniele Alessandri e3e0475826 Fixed the pipline example in the README. 2009-12-26 19:17:48 +01:00
Daniele Alessandri 767c488d35 Updated README to reflect the differences of the PHP 5.2 version of Predis. 2009-12-26 18:59:14 +01:00
Daniele Alessandri 1205026fb1 Backported changes from the mainline library to the PHP 5.2 branch (up to commit 6e39491) 2009-12-26 18:22:39 +01:00
Daniele Alessandri 9c314c5b5f Backported changes from the mainline library to the PHP 5.2 branch. 2009-12-16 16:12:51 +01:00
Daniele Alessandri d05a62e82c Updated the examples to match the PHP 5.2 version. 2009-12-04 22:10:54 +01:00
Daniele Alessandri 6769e0de56 First drop of Predis backported to PHP 5.2.x. The library seems to work but it is completely untested (the test suite needs to be backported too). 2009-12-04 22:08:08 +01:00
16 changed files with 4410 additions and 1059 deletions
+124
View File
@@ -0,0 +1,124 @@
v0.6.0 (2010-05-24)
* Switched to the new multi-bulk request protocol for all of the commands
in the Redis 1.2 and Redis 2.0 profiles. Inline and bulk requests are now
deprecated as they will be removed in future releases of Redis.
* The default server profile is "2.0" (targeting Redis 2.0.x). If you are
using older versions of Redis, it is highly recommended that you specify
which server profile the client should use (e.g. "1.2" when connecting
to instances of Redis 1.2.x).
* Support for Redis 1.0 is now optional and it is provided by requiring
'Predis_Compatibility.php' before creating an instance of Predis_Client.
* New commands added to the Redis 2.0 profile since Predis 0.5.1:
- Strings: SETEX, APPEND, SUBSTR
- ZSets : ZCOUNT, ZRANK, ZUNIONSTORE, ZINTERSTORE, ZREMBYRANK, ZREVRANK
- Hashes : HSET, HSETNX, HMSET, HINCRBY, HGET, HMGET, HDEL, HEXISTS,
HLEN, HKEYS, HVALS, HGETALL
- PubSub : PUBLISH, SUBSCRIBE, UNSUBSCRIBE
- Misc. : DISCARD, CONFIG
* Introduced client-level options with the new Predis_ClientOptions class.
Options can be passed to Predis_Client::__construct in its second argument
as an array or an instance of Predis_ClientOptions. For brevity's sake and
compatibility with older versions, the constructor of Predis_Client still
accepts an instance of Predis_RedisServerProfile in its second argument.
The currently supported client options are:
- profile [default: "2.0" as of Predis 0.6.0]
specifies which server profile to use when connecting to Redis. This
option accepts an instance of Predis_RedisServerProfile or a string
that indicates the target version.
- key_distribution [default: Predis_Distribution_HashRing]
specifies which key distribution strategy to use to distribute keys
among the servers that compose a cluster. This option accepts an
instance of Predis_Distribution_IDistributionStrategy so that users
can implement their own key distribution strategy. Optionally, the new
Predis_Distribution_KetamaPureRing class also provides a pure-PHP
implementation of the same algorithm used by libketama.
- throw_on_error [default: TRUE]
server errors can optionally be handled "silently": instead of throwing
an exception, the client returns an error response type.
- iterable_multibulk [EXPERIMENTAL - default: FALSE]
in addition to the classic way of fetching a whole multibulk reply
into an array, the client can now optionally stream a multibulk reply
down to the user code by using PHP iterators. It is just a little bit
slower, but it can save a lot of memory in certain scenarios.
* New parameters for connections:
- alias [default: not set]
every connection can now be identified by an alias that is useful to
get a certain connection when connected to a cluster of Redis servers.
- weight [default: not set]
allows the client to balance the keys asymmetrically across multiple
servers. This might be useful when you have servers with different
amounts of memory and you want to distribute the load of your keys
accordingly.
- connection_async [default: FALSE]
estabilish connections to servers in a non-blocking way, so that the
client is not blocked while the underlying resource performs the actual
connection.
- connection_persistent [default: FALSE]
the underlying connection resource is left open when a script ends its
lifecycle. Persistent connections can lead to unpredictable or strange
behaviours, so they should be used with extreme care.
* Introduced the Predis_Pipeline_IPipelineExecutor interface. Classes that
implements this interface are used internally by the Predis_CommandPipeline
class to change the behaviour of the pipeline when writing/reading commands
from one or multiple servers. Here is the list of the default executors:
- Predis_Pipeline_StandardExecutor
Exceptions generated by server errors might be thrown depending on the
options passed to the client (see "throw_on_error"). Instead, protocol
or network errors always throw exceptions. This is the default executor
for single and clustered connections and shares the same behaviour of
Predis 0.5.x.
- Predis_Pipeline_SafeExecutor
Exceptions generated by server, protocol or network errors are not
thrown, instead they are returned in the response array as instances of
ResponseError or CommunicationException.
- Predis_Pipeline_SafeClusterExecutor
This executor shares the same behaviour of Predis_Pipeline_SafeExecutor
but it is geared towards clustered connections.
* Support for PUBSUB is handled by the new Predis_PubSubContext class, which
could also be used to build a callback dispatcher for PUBSUB scenarios.
* When connected to a cluster of connections, it is now possible to get a
new Predis_Client instance for a single connection of the cluster by
passing its alias/index to the new Predis_Client::getClientFor() method.
* CommandPipeline and MultiExecBlock return their instances when invoking
commands, thus allowing method chaining in pipelines and multi-exec blocks.
* MultiExecBlock instances can handle the new DISCARD command.
* Connections now support float values for the connection_timeout parameter
to express timeouts with a microsecond resolution.
* FIX: TCP connections now respect the read/write timeout parameter when
reading the payload of server responses. Previously, stream_get_contents()
was being used internally to read data from a connection but it looks like
PHP does not honour the specified timeout for socket streams when inside
this function.
* FIX: The GET parameter for the SORT command now accepts also multiple key
patterns by passing an array of strings. (ISSUE #1).
* FIX: Replies to the DEL command return the number of elements deleted by
the server and not 0 or 1 interpreted as a boolean response. (ISSUE #4).
v0.5.1 (2010-01-23)
* RPOPLPUSH has been changed from bulk command to inline command in Redis
1.2.1, so ListPopLastPushHead now extends InlineCommand. The old RPOPLPUSH
behavior is still available via the ListPopLastPushHeadBulk class so that
you can override the server profile if you need the old (and uncorrect)
behaviour when connecting to a Redis 1.2.0 instance.
* Added missing support for BGREWRITEAOF for Redis >= 1.2.0
* Implemented a factory method for the RedisServerProfile class to ease the
creation of new server profile instances based on a version string.
v0.5.0 (2010-01-09)
* First versioned release of Predis
+2 -2
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009 Daniele Alessandri
Copyright (c) 2009-2010 Daniele Alessandri
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
@@ -19,4 +19,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
OTHER DEALINGS IN THE SOFTWARE.
+40 -29
View File
@@ -3,32 +3,38 @@
## About ##
Predis is a flexible and feature-complete PHP client library for the Redis key-value
database.
database. It currently comes in two flavors:
Predis is currently a work-in-progress and it targets PHP >= 5.3, though it is highly
due to be backported to PHP >= 5.2.6 as soon as the public API and the internal design
on the main branch will be considered stable enough.
- the mainline client library, which targets PHP 5.3.x and leverages a lot of the
features introduced in this new version of the PHP interpreter.
- a backport to PHP 5.2.x for those who can not upgrade their environment yet
(it admittedly has a lower priority compared to the mainline library, although we
try to keep the two versions aligned as much as possible).
Please refer to the TODO file to see which issues are still pending and what is due
to be implemented soon in Predis.
## Features ##
## Main features ##
- Client-side sharding (support for consistent hashing of keys)
- Command pipelining on single and multiple connections (transparent)
- Lazy connections (connections to Redis instances are only established just in time)
- Flexible system to define and register your own set of commands to a client instance
- Full support for Redis 2.0. Different versions of Redis are supported via server profiles.
- Client-side sharding (support for consistent hashing and custom distribution strategies).
- Command pipelining on single and multiple connections (transparent).
- Lazy connections (connections to Redis instances are only established just in time).
- Flexible system to define and register your own set of commands to a client instance.
## Quick examples ##
See the [official wiki](http://wiki.github.com/nrk/predis) of the project for a more
complete coverage of all the features available in Predis.
### Connecting to a local instance of Redis ###
You don't have to specify a tcp host and port when connecting to Redis instances
running on the localhost on the default port:
$redis = new Predis\Client();
$redis = new Predis_Client();
$redis->set('library', 'predis');
$value = $redis->get('library');
@@ -38,13 +44,13 @@ running on the localhost on the default port:
Pipelining helps with performances when there is the need to issue many commands
to a server in one go:
$redis = new Predis\Client('10.0.0.1', 6379);
$replies = $redis->pipeline(function($pipe) {
$pipe->ping();
$pipe->incrby('counter', 10);
$pipe->incrby('counter', 30);
$pipe->get('counter');
});
$redis = new Predis_Client('redis://10.0.0.1:6379/');
$pipe = $redis->pipeline();
$pipe->ping();
$pipe->incrby('counter', 10);
$pipe->incrby('counter', 30);
$pipe->get('counter');
$replies = $pipe->execute();
### Pipelining multiple commands to multiple instances of Redis (sharding) ##
@@ -54,17 +60,17 @@ Furthermore, a pipeline can be initialized on a cluster of redis instances in th
same exact way they are created on single connection. Sharding is still transparent
to the user:
$redis = Predis\Client::create(
$redis = Predis_Client::create(
array('host' => '10.0.0.1', 'port' => 6379),
array('host' => '10.0.0.2', 'port' => 6379)
);
$replies = $redis->pipeline(function($pipe) {
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", str_pad($i, 4, '0', 0));
$pipe->get("key:$i");
}
});
$pipe = $redis->pipeline();
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", str_pad($i, 4, '0', 0));
$pipe->get("key:$i");
}
$replies = $pipe->flushPipeline();
### Definition and runtime registration of new commands on the client ###
@@ -76,12 +82,12 @@ its way into a stable Predis release, then you can start off by creating a new
class that matches the command type and its behaviour and then bind it to a
client instance at runtime. Actually, it is easier done than said:
class BrandNewRedisCommand extends \Predis\InlineCommand {
class BrandNewRedisCommand extends Predis_MultiBulkCommand {
public function getCommandId() { return 'NEWCMD'; }
}
$redis = new Predis\Client();
$redis->registerCommand('BrandNewRedisCommand', 'newcmd');
$redis = new Predis_Client();
$redis->getProfile()->registerCommand('BrandNewRedisCommand', 'newcmd');
$redis->newcmd();
@@ -100,18 +106,20 @@ they are not the preferred way to contribute to Predis.
When modifying Predis please be sure that no warnings or notices are emitted by PHP
by running the interpreter in your development environment with the "error_reporting"
variable set to E_ALL.
variable set to E_ALL | E_STRICT.
## Dependencies ##
- PHP >= 5.3
- PHP >= 5.3.0 (for the mainline client library)
- PHP >= 5.2.6 (for the backported client library)
- PHPUnit (needed to run the test suite)
## Links ##
### Project ###
- [Source code](http://github.com/nrk/predis/)
- [Wiki](http://wiki.github.com/nrk/predis/)
- [Issue tracker](http://github.com/nrk/predis/issues)
### Related ###
@@ -124,6 +132,9 @@ variable set to E_ALL.
[Daniele Alessandri](mailto:suppakilla@gmail.com)
## Contributors ##
[Lorenzo Castelli](http://github.com/lcastelli)
## License ##
+5 -19
View File
@@ -1,21 +1,7 @@
* Authentication and database selection should be handled transparently by
the client.
* Documentation! The README is obviously not enought to show how to use
Predis as it does not cover all of its features.
* The current behaviour of sending, by default, unshardable commands to the
first registered connection of a ConnectionCluster instance needs to be
verified.
* The included test suite covers almost all the Redis server commands, but a
full battery of tests targeting specific functions of this library is still
missing.
* Support for pipelining commands on one or more connections works, but it
could be optimized for better performances with a cache of computed commands
hashes, but the memory impact still needs to be evalued.
* Add the possibility of flushing the command buffer from inside of a pipeline.
* Switching to/from instances of Connection and ConnectionCluster should be
transparent to the user. Using a ConnectionCluster instance when there is
only one active connection has an unnecessary overhead.
* Add more tests targeting specific functions of the library.
* Missing tests for commands:
PUBLISH, SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE
+1
View File
@@ -0,0 +1 @@
0.6.0
+12 -12
View File
@@ -1,20 +1,20 @@
<?php
require_once 'SharedConfigurations.php';
// when you have a whole set of consecutive commands to send to
// When you have a whole set of consecutive commands to send to
// a redis server, you can use a pipeline to improve performances.
$redis = Predis\Client::create($configurations);
$redis = new Predis_Client($single_server);
$replies = $redis->pipeline(function($pipe) {
$pipe->ping();
$pipe->flushdb();
$pipe->incrby('counter', 10);
$pipe->incrby('counter', 30);
$pipe->exists('counter');
$pipe->get('counter');
$pipe->mget('does_not_exist', 'counter');
});
$pipe = $redis->pipeline();
$pipe->ping();
$pipe->flushdb();
$pipe->incrby('counter', 10);
$pipe->incrby('counter', 30);
$pipe->exists('counter');
$pipe->get('counter');
$pipe->mget('does_not_exist', 'counter');
$replies = $pipe->execute();
print_r($replies);
@@ -35,4 +35,4 @@ Array
)
*/
?>
?>
+68
View File
@@ -0,0 +1,68 @@
<?php
require_once 'SharedConfigurations.php';
// Developers can customize the distribution strategy used by the client
// to distribute keys among a cluster of servers simply by creating a class
// that implements the Predis_Distribution_IDistributionAlgorithm interface.
class NaiveDistributionStrategy
implements Predis_Distribution_IDistributionStrategy {
private $_nodes, $_nodesCount;
public function __constructor() {
$this->_nodes = array();
$this->_nodesCount = 0;
}
public function add($node, $weight = null) {
$this->_nodes[] = $node;
$this->_nodesCount++;
}
private static function array_remove($array, $value) {
$newArray = array();
foreach ($array as $k => $v) {
if ($v !== $value) {
$newArray[] = $v;
}
}
return $newArray;
}
public function remove($node) {
$this->_nodes = self::array_remove($this->_nodes, $node);
$this->_nodesCount = count($this->_nodes);
}
public function get($key) {
$count = $this->_nodesCount;
if ($count === 0) {
throw new RuntimeException('No connections');
}
return $this->_nodes[$count > 1 ? abs(crc32($key) % $count) : 0];
}
public function generateKey($value) {
return crc32($value);
}
}
$options = array(
'key_distribution' => new NaiveDistributionStrategy(),
);
$redis = new Predis_Client($multiple_servers, $options);
for ($i = 0; $i < 100; $i++) {
$redis->set("key:$i", str_pad($i, 4, '0', 0));
$redis->get("key:$i");
}
$server1 = $redis->getClientFor('first')->info();
$server2 = $redis->getClientFor('second')->info();
printf("Server '%s' has %d keys while server '%s' has %d keys.\n",
'first', $server1['db15']['keys'], 'second', $server2['db15']['keys']
);
?>
+2 -2
View File
@@ -11,7 +11,7 @@ $mkv = array(
'usr:0003' => 'Third user'
);
$redis = Predis\Client::create($configurations);
$redis = new Predis_Client($single_server);
$redis->mset($mkv);
$retval = $redis->mget(array_keys($mkv));
@@ -26,4 +26,4 @@ Array
[2] => Third user
)
*/
?>
?>
+52
View File
@@ -0,0 +1,52 @@
<?php
require_once 'SharedConfigurations.php';
// Redis 2.0 features new commands that allow clients to subscribe for
// events published on certain channels (PUBSUB).
// Create a client and disable r/w timeout on the socket
$redis = new Predis_Client($single_server + array('read_write_timeout' => -1));
// Initialize a new pubsub context
$pubsub = $redis->pubSubContext();
// Subscribe to your channels
$pubsub->subscribe('control_channel');
$pubsub->subscribe('notifications');
// Start processing the pubsup messages. Open a terminal and use redis-cli
// to push messages to the channels. Examples:
// ./redis-cli PUBLISH notifications "this is a test"
// ./redis-cli PUBLISH control_channel quit_loop
foreach ($pubsub as $message) {
switch ($message->kind) {
case 'subscribe':
echo "Subscribed to {$message->channel}\n";
break;
case 'message':
if ($message->channel == 'control_channel') {
if ($message->payload == 'quit_loop') {
echo "Aborting pubsub loop...\n";
$pubsub->unsubscribe();
}
else {
echo "Received an unrecognized command: {$message->payload}.\n";
}
}
else {
echo "Received the following message from {$message->channel}:\n",
" {$message->payload}\n\n";
}
break;
}
}
// Always unset the pubsub context instance when you are done! The
// class destructor will take care of cleanups and prevent protocol
// desynchronizations between the client and the server.
unset($pubsub);
// Say goodbye :-)
$info = $redis->info();
print_r("Goodbye from Redis v{$info['redis_version']}!\n");
?>
+17 -2
View File
@@ -1,9 +1,24 @@
<?php
require_once '../lib/Predis.php';
$configurations = array(
$single_server = array(
'host' => '127.0.0.1',
'port' => 6379,
'database' => 15
);
?>
$multiple_servers = array(
array(
'host' => '127.0.0.1',
'port' => 6379,
'database' => 15,
'alias' => 'first',
),
array(
'host' => '127.0.0.1',
'port' => 6380,
'database' => 15,
'alias' => 'second',
),
);
?>
+2 -2
View File
@@ -3,7 +3,7 @@ require_once 'SharedConfigurations.php';
// simple set and get scenario
$redis = Predis\Client::create($configurations);
$redis = new Predis_Client($single_server);
$redis->set('library', 'predis');
$retval = $redis->get('library');
@@ -13,4 +13,4 @@ print_r($retval);
/* OUTPUT
predis
*/
?>
?>
+2028 -548
View File
File diff suppressed because it is too large Load Diff
+496
View File
@@ -0,0 +1,496 @@
<?php
Predis_RedisServerProfile::registerProfile('Predis_RedisServer_v1_0', '1.0');
class Predis_RedisServer_v1_0 extends Predis_RedisServerProfile {
public function getVersion() { return '1.0'; }
public function getSupportedCommands() {
return array(
/* miscellaneous commands */
'ping' => 'Predis_Compatibility_v1_0_Commands_Ping',
'echo' => 'Predis_Compatibility_v1_0_Commands_DoEcho',
'auth' => 'Predis_Compatibility_v1_0_Commands_Auth',
/* connection handling */
'quit' => 'Predis_Compatibility_v1_0_Commands_Quit',
/* commands operating on string values */
'set' => 'Predis_Compatibility_v1_0_Commands_Set',
'setnx' => 'Predis_Compatibility_v1_0_Commands_SetPreserve',
'setPreserve' => 'Predis_Compatibility_v1_0_Commands_SetPreserve',
'get' => 'Predis_Compatibility_v1_0_Commands_Get',
'mget' => 'Predis_Compatibility_v1_0_Commands_GetMultiple',
'getMultiple' => 'Predis_Compatibility_v1_0_Commands_GetMultiple',
'getset' => 'Predis_Compatibility_v1_0_Commands_GetSet',
'getSet' => 'Predis_Compatibility_v1_0_Commands_GetSet',
'incr' => 'Predis_Compatibility_v1_0_Commands_Increment',
'increment' => 'Predis_Compatibility_v1_0_Commands_Increment',
'incrby' => 'Predis_Compatibility_v1_0_Commands_IncrementBy',
'incrementBy' => 'Predis_Compatibility_v1_0_Commands_IncrementBy',
'decr' => 'Predis_Compatibility_v1_0_Commands_Decrement',
'decrement' => 'Predis_Compatibility_v1_0_Commands_Decrement',
'decrby' => 'Predis_Compatibility_v1_0_Commands_DecrementBy',
'decrementBy' => 'Predis_Compatibility_v1_0_Commands_DecrementBy',
'exists' => 'Predis_Compatibility_v1_0_Commands_Exists',
'del' => 'Predis_Compatibility_v1_0_Commands_Delete',
'delete' => 'Predis_Compatibility_v1_0_Commands_Delete',
'type' => 'Predis_Compatibility_v1_0_Commands_Type',
/* commands operating on the key space */
'keys' => 'Predis_Compatibility_v1_0_Commands_Keys',
'randomkey' => 'Predis_Compatibility_v1_0_Commands_RandomKey',
'randomKey' => 'Predis_Compatibility_v1_0_Commands_RandomKey',
'rename' => 'Predis_Compatibility_v1_0_Commands_Rename',
'renamenx' => 'Predis_Compatibility_v1_0_Commands_RenamePreserve',
'renamePreserve' => 'Predis_Compatibility_v1_0_Commands_RenamePreserve',
'expire' => 'Predis_Compatibility_v1_0_Commands_Expire',
'expireat' => 'Predis_Compatibility_v1_0_Commands_ExpireAt',
'expireAt' => 'Predis_Compatibility_v1_0_Commands_ExpireAt',
'dbsize' => 'Predis_Compatibility_v1_0_Commands_DatabaseSize',
'databaseSize' => 'Predis_Compatibility_v1_0_Commands_DatabaseSize',
'ttl' => 'Predis_Compatibility_v1_0_Commands_TimeToLive',
'timeToLive' => 'Predis_Compatibility_v1_0_Commands_TimeToLive',
/* commands operating on lists */
'rpush' => 'Predis_Compatibility_v1_0_Commands_ListPushTail',
'pushTail' => 'Predis_Compatibility_v1_0_Commands_ListPushTail',
'lpush' => 'Predis_Compatibility_v1_0_Commands_ListPushHead',
'pushHead' => 'Predis_Compatibility_v1_0_Commands_ListPushHead',
'llen' => 'Predis_Compatibility_v1_0_Commands_ListLength',
'listLength' => 'Predis_Compatibility_v1_0_Commands_ListLength',
'lrange' => 'Predis_Compatibility_v1_0_Commands_ListRange',
'listRange' => 'Predis_Compatibility_v1_0_Commands_ListRange',
'ltrim' => 'Predis_Compatibility_v1_0_Commands_ListTrim',
'listTrim' => 'Predis_Compatibility_v1_0_Commands_ListTrim',
'lindex' => 'Predis_Compatibility_v1_0_Commands_ListIndex',
'listIndex' => 'Predis_Compatibility_v1_0_Commands_ListIndex',
'lset' => 'Predis_Compatibility_v1_0_Commands_ListSet',
'listSet' => 'Predis_Compatibility_v1_0_Commands_ListSet',
'lrem' => 'Predis_Compatibility_v1_0_Commands_ListRemove',
'listRemove' => 'Predis_Compatibility_v1_0_Commands_ListRemove',
'lpop' => 'Predis_Compatibility_v1_0_Commands_ListPopFirst',
'popFirst' => 'Predis_Compatibility_v1_0_Commands_ListPopFirst',
'rpop' => 'Predis_Compatibility_v1_0_Commands_ListPopLast',
'popLast' => 'Predis_Compatibility_v1_0_Commands_ListPopLast',
/* commands operating on sets */
'sadd' => 'Predis_Compatibility_v1_0_Commands_SetAdd',
'setAdd' => 'Predis_Compatibility_v1_0_Commands_SetAdd',
'srem' => 'Predis_Compatibility_v1_0_Commands_SetRemove',
'setRemove' => 'Predis_Compatibility_v1_0_Commands_SetRemove',
'spop' => 'Predis_Compatibility_v1_0_Commands_SetPop',
'setPop' => 'Predis_Compatibility_v1_0_Commands_SetPop',
'smove' => 'Predis_Compatibility_v1_0_Commands_SetMove',
'setMove' => 'Predis_Compatibility_v1_0_Commands_SetMove',
'scard' => 'Predis_Compatibility_v1_0_Commands_SetCardinality',
'setCardinality' => 'Predis_Compatibility_v1_0_Commands_SetCardinality',
'sismember' => 'Predis_Compatibility_v1_0_Commands_SetIsMember',
'setIsMember' => 'Predis_Compatibility_v1_0_Commands_SetIsMember',
'sinter' => 'Predis_Compatibility_v1_0_Commands_SetIntersection',
'setIntersection' => 'Predis_Compatibility_v1_0_Commands_SetIntersection',
'sinterstore' => 'Predis_Compatibility_v1_0_Commands_SetIntersectionStore',
'setIntersectionStore' => 'Predis_Compatibility_v1_0_Commands_SetIntersectionStore',
'sunion' => 'Predis_Compatibility_v1_0_Commands_SetUnion',
'setUnion' => 'Predis_Compatibility_v1_0_Commands_SetUnion',
'sunionstore' => 'Predis_Compatibility_v1_0_Commands_SetUnionStore',
'setUnionStore' => 'Predis_Compatibility_v1_0_Commands_SetUnionStore',
'sdiff' => 'Predis_Compatibility_v1_0_Commands_SetDifference',
'setDifference' => 'Predis_Compatibility_v1_0_Commands_SetDifference',
'sdiffstore' => 'Predis_Compatibility_v1_0_Commands_SetDifferenceStore',
'setDifferenceStore' => 'Predis_Compatibility_v1_0_Commands_SetDifferenceStore',
'smembers' => 'Predis_Compatibility_v1_0_Commands_SetMembers',
'setMembers' => 'Predis_Compatibility_v1_0_Commands_SetMembers',
'srandmember' => 'Predis_Compatibility_v1_0_Commands_SetRandomMember',
'setRandomMember' => 'Predis_Compatibility_v1_0_Commands_SetRandomMember',
/* multiple databases handling commands */
'select' => 'Predis_Compatibility_v1_0_Commands_SelectDatabase',
'selectDatabase' => 'Predis_Compatibility_v1_0_Commands_SelectDatabase',
'move' => 'Predis_Compatibility_v1_0_Commands_MoveKey',
'moveKey' => 'Predis_Compatibility_v1_0_Commands_MoveKey',
'flushdb' => 'Predis_Compatibility_v1_0_Commands_FlushDatabase',
'flushDatabase' => 'Predis_Compatibility_v1_0_Commands_FlushDatabase',
'flushall' => 'Predis_Compatibility_v1_0_Commands_FlushAll',
'flushDatabases' => 'Predis_Compatibility_v1_0_Commands_FlushAll',
/* sorting */
'sort' => 'Predis_Compatibility_v1_0_Commands_Sort',
/* remote server control commands */
'info' => 'Predis_Compatibility_v1_0_Commands_Info',
'slaveof' => 'Predis_Compatibility_v1_0_Commands_SlaveOf',
'slaveOf' => 'Predis_Compatibility_v1_0_Commands_SlaveOf',
/* persistence control commands */
'save' => 'Predis_Compatibility_v1_0_Commands_Save',
'bgsave' => 'Predis_Compatibility_v1_0_Commands_BackgroundSave',
'backgroundSave' => 'Predis_Compatibility_v1_0_Commands_BackgroundSave',
'lastsave' => 'Predis_Compatibility_v1_0_Commands_LastSave',
'lastSave' => 'Predis_Compatibility_v1_0_Commands_LastSave',
'shutdown' => 'Predis_Compatibility_v1_0_Commands_Shutdown',
);
}
}
/* ------------------------------------------------------------------------- */
/* miscellaneous commands */
class Predis_Compatibility_v1_0_Commands_Ping extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'PING'; }
public function parseResponse($data) {
return $data === 'PONG' ? true : false;
}
}
class Predis_Compatibility_v1_0_Commands_DoEcho extends Predis_BulkCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'ECHO'; }
}
class Predis_Compatibility_v1_0_Commands_Auth extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'AUTH'; }
}
/* connection handling */
class Predis_Compatibility_v1_0_Commands_Quit extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'QUIT'; }
public function closesConnection() { return true; }
}
/* commands operating on string values */
class Predis_Compatibility_v1_0_Commands_Set extends Predis_BulkCommand {
public function getCommandId() { return 'SET'; }
}
class Predis_Compatibility_v1_0_Commands_SetPreserve extends Predis_BulkCommand {
public function getCommandId() { return 'SETNX'; }
public function parseResponse($data) { return (bool) $data; }
}
class Predis_Compatibility_v1_0_Commands_Get extends Predis_InlineCommand {
public function getCommandId() { return 'GET'; }
}
class Predis_Compatibility_v1_0_Commands_GetMultiple extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'MGET'; }
}
class Predis_Compatibility_v1_0_Commands_GetSet extends Predis_BulkCommand {
public function getCommandId() { return 'GETSET'; }
}
class Predis_Compatibility_v1_0_Commands_Increment extends Predis_InlineCommand {
public function getCommandId() { return 'INCR'; }
}
class Predis_Compatibility_v1_0_Commands_IncrementBy extends Predis_InlineCommand {
public function getCommandId() { return 'INCRBY'; }
}
class Predis_Compatibility_v1_0_Commands_Decrement extends Predis_InlineCommand {
public function getCommandId() { return 'DECR'; }
}
class Predis_Compatibility_v1_0_Commands_DecrementBy extends Predis_InlineCommand {
public function getCommandId() { return 'DECRBY'; }
}
class Predis_Compatibility_v1_0_Commands_Exists extends Predis_InlineCommand {
public function getCommandId() { return 'EXISTS'; }
public function parseResponse($data) { return (bool) $data; }
}
class Predis_Compatibility_v1_0_Commands_Delete extends Predis_InlineCommand {
public function getCommandId() { return 'DEL'; }
}
class Predis_Compatibility_v1_0_Commands_Type extends Predis_InlineCommand {
public function getCommandId() { return 'TYPE'; }
}
/* commands operating on the key space */
class Predis_Compatibility_v1_0_Commands_Keys extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'KEYS'; }
public function parseResponse($data) {
return strlen($data) > 0 ? explode(' ', $data) : array();
}
}
class Predis_Compatibility_v1_0_Commands_RandomKey extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'RANDOMKEY'; }
public function parseResponse($data) { return $data !== '' ? $data : null; }
}
class Predis_Compatibility_v1_0_Commands_Rename extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'RENAME'; }
}
class Predis_Compatibility_v1_0_Commands_RenamePreserve extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'RENAMENX'; }
public function parseResponse($data) { return (bool) $data; }
}
class Predis_Compatibility_v1_0_Commands_Expire extends Predis_InlineCommand {
public function getCommandId() { return 'EXPIRE'; }
public function parseResponse($data) { return (bool) $data; }
}
class Predis_Compatibility_v1_0_Commands_ExpireAt extends Predis_InlineCommand {
public function getCommandId() { return 'EXPIREAT'; }
public function parseResponse($data) { return (bool) $data; }
}
class Predis_Compatibility_v1_0_Commands_DatabaseSize extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'DBSIZE'; }
}
class Predis_Compatibility_v1_0_Commands_TimeToLive extends Predis_InlineCommand {
public function getCommandId() { return 'TTL'; }
}
/* commands operating on lists */
class Predis_Compatibility_v1_0_Commands_ListPushTail extends Predis_BulkCommand {
public function getCommandId() { return 'RPUSH'; }
}
class Predis_Compatibility_v1_0_Commands_ListPushHead extends Predis_BulkCommand {
public function getCommandId() { return 'LPUSH'; }
}
class Predis_Compatibility_v1_0_Commands_ListLength extends Predis_InlineCommand {
public function getCommandId() { return 'LLEN'; }
}
class Predis_Compatibility_v1_0_Commands_ListRange extends Predis_InlineCommand {
public function getCommandId() { return 'LRANGE'; }
}
class Predis_Compatibility_v1_0_Commands_ListTrim extends Predis_InlineCommand {
public function getCommandId() { return 'LTRIM'; }
}
class Predis_Compatibility_v1_0_Commands_ListIndex extends Predis_InlineCommand {
public function getCommandId() { return 'LINDEX'; }
}
class Predis_Compatibility_v1_0_Commands_ListSet extends Predis_BulkCommand {
public function getCommandId() { return 'LSET'; }
}
class Predis_Compatibility_v1_0_Commands_ListRemove extends Predis_BulkCommand {
public function getCommandId() { return 'LREM'; }
}
class Predis_Compatibility_v1_0_Commands_ListPopFirst extends Predis_InlineCommand {
public function getCommandId() { return 'LPOP'; }
}
class Predis_Compatibility_v1_0_Commands_ListPopLast extends Predis_InlineCommand {
public function getCommandId() { return 'RPOP'; }
}
/* commands operating on sets */
class Predis_Compatibility_v1_0_Commands_SetAdd extends Predis_BulkCommand {
public function getCommandId() { return 'SADD'; }
public function parseResponse($data) { return (bool) $data; }
}
class Predis_Compatibility_v1_0_Commands_SetRemove extends Predis_BulkCommand {
public function getCommandId() { return 'SREM'; }
public function parseResponse($data) { return (bool) $data; }
}
class Predis_Compatibility_v1_0_Commands_SetPop extends Predis_InlineCommand {
public function getCommandId() { return 'SPOP'; }
}
class Predis_Compatibility_v1_0_Commands_SetMove extends Predis_BulkCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SMOVE'; }
public function parseResponse($data) { return (bool) $data; }
}
class Predis_Compatibility_v1_0_Commands_SetCardinality extends Predis_InlineCommand {
public function getCommandId() { return 'SCARD'; }
}
class Predis_Compatibility_v1_0_Commands_SetIsMember extends Predis_BulkCommand {
public function getCommandId() { return 'SISMEMBER'; }
public function parseResponse($data) { return (bool) $data; }
}
class Predis_Compatibility_v1_0_Commands_SetIntersection extends Predis_InlineCommand {
public function getCommandId() { return 'SINTER'; }
}
class Predis_Compatibility_v1_0_Commands_SetIntersectionStore extends Predis_InlineCommand {
public function getCommandId() { return 'SINTERSTORE'; }
}
class Predis_Compatibility_v1_0_Commands_SetUnion extends Predis_InlineCommand {
public function getCommandId() { return 'SUNION'; }
}
class Predis_Compatibility_v1_0_Commands_SetUnionStore extends Predis_InlineCommand {
public function getCommandId() { return 'SUNIONSTORE'; }
}
class Predis_Compatibility_v1_0_Commands_SetDifference extends Predis_InlineCommand {
public function getCommandId() { return 'SDIFF'; }
}
class Predis_Compatibility_v1_0_Commands_SetDifferenceStore extends Predis_InlineCommand {
public function getCommandId() { return 'SDIFFSTORE'; }
}
class Predis_Compatibility_v1_0_Commands_SetMembers extends Predis_InlineCommand {
public function getCommandId() { return 'SMEMBERS'; }
}
class Predis_Compatibility_v1_0_Commands_SetRandomMember extends Predis_InlineCommand {
public function getCommandId() { return 'SRANDMEMBER'; }
}
/* multiple databases handling commands */
class Predis_Compatibility_v1_0_Commands_SelectDatabase extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SELECT'; }
}
class Predis_Compatibility_v1_0_Commands_MoveKey extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'MOVE'; }
public function parseResponse($data) { return (bool) $data; }
}
class Predis_Compatibility_v1_0_Commands_FlushDatabase extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'FLUSHDB'; }
}
class Predis_Compatibility_v1_0_Commands_FlushAll extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'FLUSHALL'; }
}
/* sorting */
class Predis_Compatibility_v1_0_Commands_Sort extends Predis_InlineCommand {
public function getCommandId() { return 'SORT'; }
public function filterArguments(Array $arguments) {
if (count($arguments) === 1) {
return $arguments;
}
// TODO: add more parameters checks
$query = array($arguments[0]);
$sortParams = $arguments[1];
if (isset($sortParams['by'])) {
$query[] = 'BY';
$query[] = $sortParams['by'];
}
if (isset($sortParams['get'])) {
$getargs = $sortParams['get'];
if (is_array($getargs)) {
foreach ($getargs as $getarg) {
$query[] = 'GET';
$query[] = $getarg;
}
}
else {
$query[] = 'GET';
$query[] = $getargs;
}
}
if (isset($sortParams['limit']) && is_array($sortParams['limit'])) {
$query[] = 'LIMIT';
$query[] = $sortParams['limit'][0];
$query[] = $sortParams['limit'][1];
}
if (isset($sortParams['sort'])) {
$query[] = strtoupper($sortParams['sort']);
}
if (isset($sortParams['alpha']) && $sortParams['alpha'] == true) {
$query[] = 'ALPHA';
}
if (isset($sortParams['store']) && $sortParams['store'] == true) {
$query[] = 'STORE';
$query[] = $sortParams['store'];
}
return $query;
}
}
/* persistence control commands */
class Predis_Compatibility_v1_0_Commands_Save extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SAVE'; }
}
class Predis_Compatibility_v1_0_Commands_BackgroundSave extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'BGSAVE'; }
public function parseResponse($data) {
if ($data == 'Background saving started') {
return true;
}
return $data;
}
}
class Predis_Compatibility_v1_0_Commands_LastSave extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'LASTSAVE'; }
}
class Predis_Compatibility_v1_0_Commands_Shutdown extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SHUTDOWN'; }
public function closesConnection() { return true; }
}
/* remote server control commands */
class Predis_Compatibility_v1_0_Commands_Info extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'INFO'; }
public function parseResponse($data) {
$info = array();
$infoLines = explode("\r\n", $data, -1);
foreach ($infoLines as $row) {
list($k, $v) = explode(':', $row);
if (!preg_match('/^db\d+$/', $k)) {
$info[$k] = $v;
}
else {
$db = array();
foreach (explode(',', $v) as $dbvar) {
list($dbvk, $dbvv) = explode('=', $dbvar);
$db[trim($dbvk)] = $dbvv;
}
$info[$k] = $db;
}
}
return $info;
}
}
class Predis_Compatibility_v1_0_Commands_SlaveOf extends Predis_InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SLAVEOF'; }
public function filterArguments(Array $arguments) {
if (count($arguments) === 0 || $arguments[0] === 'NO ONE') {
return array('NO', 'ONE');
}
return $arguments;
}
}
?>
+503
View File
@@ -0,0 +1,503 @@
<?php
define('I_AM_AWARE_OF_THE_DESTRUCTIVE_POWER_OF_THIS_TEST_SUITE', false);
require_once 'PHPUnit/Framework.php';
require_once 'PredisShared.php';
require_once '../lib/Predis_Compatibility.php';
class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
public $redis;
protected function setUp() {
$this->redis = RC::getConnection();
$this->redis->flushDatabase();
}
protected function tearDown() {
}
protected function onNotSuccessfulTest(Exception $exception) {
// drops and reconnect to a redis server on uncaught exceptions
RC::resetConnection();
parent::onNotSuccessfulTest($exception);
}
/* ConnectionParameters */
function testConnectionParametersDefaultValues() {
$params = new Predis_ConnectionParameters();
$this->assertEquals(Predis_ConnectionParameters::DEFAULT_HOST, $params->host);
$this->assertEquals(Predis_ConnectionParameters::DEFAULT_PORT, $params->port);
$this->assertEquals(Predis_ConnectionParameters::DEFAULT_TIMEOUT, $params->connection_timeout);
$this->assertNull($params->read_write_timeout);
$this->assertNull($params->database);
$this->assertNull($params->password);
$this->assertNull($params->alias);
}
function testConnectionParametersSetupValuesArray() {
$paramsArray = RC::getConnectionParametersArgumentsArray();
$params = new Predis_ConnectionParameters($paramsArray);
$this->assertEquals($paramsArray['host'], $params->host);
$this->assertEquals($paramsArray['port'], $params->port);
$this->assertEquals($paramsArray['connection_timeout'], $params->connection_timeout);
$this->assertEquals($paramsArray['read_write_timeout'], $params->read_write_timeout);
$this->assertEquals($paramsArray['database'], $params->database);
$this->assertEquals($paramsArray['password'], $params->password);
$this->assertEquals($paramsArray['alias'], $params->alias);
}
function testConnectionParametersSetupValuesString() {
$paramsArray = RC::getConnectionParametersArgumentsArray();
$paramsString = RC::getConnectionParametersArgumentsString($paramsArray);
$params = new Predis_ConnectionParameters($paramsArray);
$this->assertEquals($paramsArray['host'], $params->host);
$this->assertEquals($paramsArray['port'], $params->port);
$this->assertEquals($paramsArray['connection_timeout'], $params->connection_timeout);
$this->assertEquals($paramsArray['read_write_timeout'], $params->read_write_timeout);
$this->assertEquals($paramsArray['database'], $params->database);
$this->assertEquals($paramsArray['password'], $params->password);
$this->assertEquals($paramsArray['alias'], $params->alias);
}
/* Command and derivates */
function testCommand_TestArguments() {
$cmdArgs = array('key1', 'key2', 'key3');
$cmd = new Predis_Commands_GetMultiple();
$cmd->setArgumentsArray($cmdArgs);
$this->assertEquals($cmdArgs[0], $cmd->getArgument(0));
$this->assertEquals($cmdArgs[1], $cmd->getArgument(1));
$this->assertEquals($cmdArgs[2], $cmd->getArgument(2));
$cmd = new Predis_Commands_GetMultiple();
$cmd->setArguments('key1', 'key2', 'key3');
$this->assertEquals($cmdArgs[0], $cmd->getArgument(0));
$this->assertEquals($cmdArgs[1], $cmd->getArgument(1));
$this->assertEquals($cmdArgs[2], $cmd->getArgument(2));
$cmd = new Predis_Commands_Ping();
$this->assertNull($cmd->getArgument(0));
}
function testCommand_InlineWithNoArguments() {
$cmd = new Predis_Compatibility_v1_0_Commands_Ping();
$this->assertType('Predis_InlineCommand', $cmd);
$this->assertEquals('PING', $cmd->getCommandId());
$this->assertFalse($cmd->closesConnection());
$this->assertFalse($cmd->canBeHashed());
$this->assertNull($cmd->getHash(new Predis_Distribution_HashRing()));
$this->assertEquals("PING\r\n", $cmd->invoke());
}
function testCommand_InlineWithArguments() {
$cmd = new Predis_Compatibility_v1_0_Commands_Get();
$cmd->setArgumentsArray(array('key'));
$this->assertType('Predis_InlineCommand', $cmd);
$this->assertEquals('GET', $cmd->getCommandId());
$this->assertFalse($cmd->closesConnection());
$this->assertTrue($cmd->canBeHashed());
$this->assertNotNull($cmd->getHash(new Predis_Distribution_HashRing()));
$this->assertEquals("GET key\r\n", $cmd->invoke());
}
function testCommand_BulkWithArguments() {
$cmd = new Predis_Compatibility_v1_0_Commands_Set();
$cmd->setArgumentsArray(array('key', 'value'));
$this->assertType('Predis_BulkCommand', $cmd);
$this->assertEquals('SET', $cmd->getCommandId());
$this->assertFalse($cmd->closesConnection());
$this->assertTrue($cmd->canBeHashed());
$this->assertNotNull($cmd->getHash(new Predis_Distribution_HashRing()));
$this->assertEquals("SET key 5\r\nvalue\r\n", $cmd->invoke());
}
function testCommand_MultiBulkWithArguments() {
$cmd = new Predis_Commands_SetMultiple();
$cmd->setArgumentsArray(array('key1', 'value1', 'key2', 'value2'));
$this->assertType('Predis_MultiBulkCommand', $cmd);
$this->assertEquals('MSET', $cmd->getCommandId());
$this->assertFalse($cmd->closesConnection());
$this->assertFalse($cmd->canBeHashed());
$this->assertNotNull($cmd->getHash(new Predis_Distribution_HashRing()));
$this->assertEquals("*5\r\n$4\r\nMSET\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n", $cmd->invoke());
}
function testCommand_ParseResponse() {
// default parser
$cmd = new Predis_Commands_Get();
$this->assertEquals('test', $cmd->parseResponse('test'));
// overridden parser (boolean)
$cmd = new Predis_Commands_Exists();
$this->assertTrue($cmd->parseResponse('1'));
$this->assertFalse($cmd->parseResponse('0'));
// overridden parser (boolean)
$cmd = new Predis_Commands_Ping();
$this->assertTrue($cmd->parseResponse('PONG'));
// overridden parser (complex)
// TODO: emulate a respons to INFO
}
/* RedisServerProfile and derivates */
function testRedisServerProfile_GetSpecificVersions() {
$this->assertType('Predis_RedisServer_v1_0', Predis_RedisServerProfile::get('1.0'));
$this->assertType('Predis_RedisServer_v1_2', Predis_RedisServerProfile::get('1.2'));
$this->assertType('Predis_RedisServer_v2_0', Predis_RedisServerProfile::get('2.0'));
$this->assertType('Predis_RedisServer_vNext', Predis_RedisServerProfile::get('dev'));
$this->assertType('Predis_RedisServerProfile', Predis_RedisServerProfile::get('default'));
$this->assertEquals(Predis_RedisServerProfile::get('default'), Predis_RedisServerProfile::getDefault());
}
function testRedisServerProfile_SupportedCommands() {
$profile_10 = Predis_RedisServerProfile::get('1.0');
$profile_12 = Predis_RedisServerProfile::get('1.2');
$this->assertTrue($profile_10->supportsCommand('info'));
$this->assertTrue($profile_12->supportsCommand('info'));
$this->assertFalse($profile_10->supportsCommand('mset'));
$this->assertTrue($profile_12->supportsCommand('mset'));
$this->assertFalse($profile_10->supportsCommand('multi'));
$this->assertFalse($profile_12->supportsCommand('multi'));
}
function testRedisServerProfile_CommandsCreation() {
$profile = Predis_RedisServerProfile::get('1.0');
$cmdNoArgs = $profile->createCommand('info');
$this->assertType('Predis_Compatibility_v1_0_Commands_Info', $cmdNoArgs);
$this->assertNull($cmdNoArgs->getArgument());
$args = array('key1', 'key2');
$cmdWithArgs = $profile->createCommand('mget', $args);
$this->assertType('Predis_Compatibility_v1_0_Commands_GetMultiple', $cmdWithArgs);
$this->assertEquals($args[0], $cmdWithArgs->getArgument()); // TODO: why?
$this->assertEquals($args[0], $cmdWithArgs->getArgument(0));
$this->assertEquals($args[1], $cmdWithArgs->getArgument(1));
$bogusCommand = 'not_existing_command';
$expectedMessage = "'$bogusCommand' is not a registered Redis command";
RC::testForClientException($this, $expectedMessage, p_anon("\$test", "
\$profile = Predis_RedisServerProfile::getDefault();
\$profile->createCommand('$bogusCommand');
"));
}
function testRedisServerProfile_CommandsRegistration() {
$profile = Predis_RedisServerProfile::get('1.0');
$cmdId = 'mset';
$cmdClass = 'Predis_Commands_SetMultiple';
$this->assertFalse($profile->supportsCommand($cmdId));
$profile->registerCommand(new $cmdClass(), $cmdId);
$this->assertTrue($profile->supportsCommand($cmdId));
$this->assertType($cmdClass, $profile->createCommand($cmdId));
}
/* ResponseQueued */
function testResponseQueued() {
$response = new Predis_ResponseQueued();
$this->assertTrue($response->queued);
$this->assertEquals(Predis_Protocol::QUEUED, (string)$response);
}
/* ResponseError */
function testResponseError() {
$errorMessage = 'ERROR MESSAGE';
$response = new Predis_ResponseError($errorMessage);
$this->assertTrue($response->error);
$this->assertEquals($errorMessage, $response->message);
$this->assertEquals($errorMessage, (string)$response);
}
/* Connection */
function testConnection_StringCastReturnsIPAndPort() {
$connection = new Predis_Connection(RC::getConnectionParameters());
$this->assertEquals(RC::SERVER_HOST . ':' . RC::SERVER_PORT, (string) $connection);
}
function testConnection_ConnectDisconnect() {
$connection = new Predis_Connection(RC::getConnectionParameters());
$this->assertFalse($connection->isConnected());
$connection->connect();
$this->assertTrue($connection->isConnected());
$connection->disconnect();
$this->assertFalse($connection->isConnected());
}
function testConnection_WriteAndReadCommand() {
$cmd = Predis_RedisServerProfile::getDefault()->createCommand('ping');
$connection = new Predis_Connection(RC::getConnectionParameters());
$connection->connect();
$connection->writeCommand($cmd);
$this->assertTrue($connection->readResponse($cmd));
}
function testConnection_WriteCommandAndCloseConnection() {
$cmd = Predis_RedisServerProfile::getDefault()->createCommand('quit');
$connection = new Predis_Connection(RC::getConnectionParameters());
$connection->connect();
$this->assertTrue($connection->isConnected());
$connection->writeCommand($cmd);
$expectedMessage = 'Error while reading line from the server';
$thrownException = null;
try {
$connection->readResponse($cmd);
}
catch (Predis_CommunicationException $exception) {
$thrownException = $exception;
}
$this->assertType('Predis_CommunicationException', $thrownException);
$this->assertEquals($expectedMessage, $thrownException->getMessage());
//$this->assertFalse($connection->isConnected());
}
function testConnection_GetSocketOpensConnection() {
$connection = new Predis_Connection(RC::getConnectionParameters());
$this->assertFalse($connection->isConnected());
$this->assertType('resource', $connection->getSocket());
$this->assertTrue($connection->isConnected());
}
function testConnection_LazyConnect() {
$cmd = Predis_RedisServerProfile::getDefault()->createCommand('ping');
$connection = new Predis_Connection(RC::getConnectionParameters());
$this->assertFalse($connection->isConnected());
$connection->writeCommand($cmd);
$this->assertTrue($connection->isConnected());
$this->assertTrue($connection->readResponse($cmd));
}
function testConnection_RawCommand() {
$connection = new Predis_Connection(RC::getConnectionParameters());
$this->assertEquals('PONG', $connection->rawCommand("PING\r\n"));
}
function testConnection_Alias() {
$connection1 = new Predis_Connection(RC::getConnectionParameters());
$this->assertNull($connection1->getParameters()->alias);
$args = array_merge(RC::getConnectionArguments(), array('alias' => 'servername'));
$connection2 = new Predis_Connection(new Predis_ConnectionParameters($args));
$this->assertEquals('servername', $connection2->getParameters()->alias);
}
function testConnection_ConnectionTimeout() {
$timeout = 3;
$args = array('host' => '1.0.0.1', 'connection_timeout' => $timeout);
$connection = new Predis_Connection(new Predis_ConnectionParameters($args));
$start = time();
$thrownException = null;
try {
$connection->connect();
}
catch (Predis_CommunicationException $exception) {
$thrownException = $exception;
}
$this->assertType('Predis_CommunicationException', $thrownException);
$this->assertEquals((float)(time() - $start), $timeout, '', 1);
}
function testConnection_ReadTimeout() {
$timeout = 1;
$args = array_merge(RC::getConnectionArguments(), array('read_write_timeout' => $timeout));
$cmdFake = Predis_RedisServerProfile::getDefault()->createCommand('ping');
$connection = new Predis_Connection(new Predis_ConnectionParameters($args));
$expectedMessage = 'Error while reading line from the server';
$start = time();
$thrownException = null;
try {
$connection->readResponse($cmdFake);
}
catch (Predis_CommunicationException $exception) {
$thrownException = $exception;
}
$this->assertType('Predis_CommunicationException', $thrownException);
$this->assertEquals($expectedMessage, $thrownException->getMessage());
$this->assertEquals((float)(time() - $start), $timeout, '', 1);
}
/* ResponseReader */
function testResponseReader_OptionIterableMultiBulkReplies() {
$connection = new Predis_Connection(RC::getConnectionParameters());
$responseReader = $connection->getResponseReader();
$responseReader->setHandler(
Predis_Protocol::PREFIX_MULTI_BULK,
new Predis_ResponseMultiBulkHandler()
);
$this->assertType('array', $connection->rawCommand("KEYS *\r\n"));
$responseReader->setHandler(
Predis_Protocol::PREFIX_MULTI_BULK,
new Predis_ResponseMultiBulkStreamHandler()
);
$this->assertType('Iterator', $connection->rawCommand("KEYS *\r\n"));
}
function testResponseReader_OptionExceptionOnError() {
$connection = new Predis_Connection(RC::getConnectionParameters());
$responseReader = $connection->getResponseReader();
$connection->rawCommand("SET key 5\r\nvalue\r\n");
$rawCmdUnexpected = "LPUSH key 5\r\nvalue\r\n";
$responseReader->setHandler(
Predis_Protocol::PREFIX_ERROR,
new Predis_ResponseErrorSilentHandler()
);
$errorReply = $connection->rawCommand($rawCmdUnexpected);
$this->assertType('Predis_ResponseError', $errorReply);
$this->assertEquals(RC::EXCEPTION_WRONG_TYPE, $errorReply->message);
$responseReader->setHandler(
Predis_Protocol::PREFIX_ERROR,
new Predis_ResponseErrorHandler()
);
$thrownException = null;
try {
$connection->rawCommand($rawCmdUnexpected);
}
catch (Predis_ServerException $exception) {
$thrownException = $exception;
}
$this->assertType('Predis_ServerException', $thrownException);
$this->assertEquals(RC::EXCEPTION_WRONG_TYPE, $thrownException->getMessage());
}
/* Client + CommandPipeline */
function testCommandPipeline_Simple() {
$client = RC::getConnection();
$client->flushdb();
$pipe = $client->pipeline();
$this->assertType('Predis_CommandPipeline', $pipe);
$this->assertType('Predis_CommandPipeline', $pipe->set('foo', 'bar'));
$this->assertType('Predis_CommandPipeline', $pipe->set('hoge', 'piyo'));
$this->assertType('Predis_CommandPipeline', $pipe->mset(array(
'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
)));
$this->assertType('Predis_CommandPipeline', $pipe->mget(array(
'foo', 'hoge', 'foofoo', 'hogehoge'
)));
$replies = $pipe->execute();
$this->assertType('array', $replies);
$this->assertEquals(4, count($replies));
$this->assertEquals(4, count($replies[3]));
$this->assertEquals('barbar', $replies[3][2]);
}
function testCommandPipeline_FluentInterface() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->pipeline()->ping()->set('foo', 'bar')->get('foo')->execute();
$this->assertType('array', $replies);
$this->assertEquals('bar', $replies[2]);
}
function testCommandPipeline_CallableAnonymousBlock() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->pipeline(p_anon("\$pipe", "
\$pipe->ping();
\$pipe->set('foo', 'bar');
\$pipe->get('foo');
"));
$this->assertType('array', $replies);
$this->assertEquals('bar', $replies[2]);
}
function testCommandPipeline_ClientExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$expectedMessage = 'TEST';
$thrownException = null;
try {
$client->pipeline(p_anon("\$pipe", "
\$pipe->ping();
\$pipe->set('foo', 'bar');
throw new Predis_ClientException('$expectedMessage');
"));
}
catch (Predis_ClientException $exception) {
$thrownException = $exception;
}
$this->assertType('Predis_ClientException', $thrownException);
$this->assertEquals($expectedMessage, $thrownException->getMessage());
$this->assertFalse($client->exists('foo'));
}
function testCommandPipeline_ServerExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$client->getResponseReader()->setHandler('-', new Predis_ResponseErrorSilentHandler());
$replies = $client->pipeline(p_anon("\$pipe", "
\$pipe->set('foo', 'bar');
\$pipe->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
\$pipe->set('hoge', 'piyo');
"));
$this->assertType('array', $replies);
$this->assertType('Predis_ResponseError', $replies[1]);
$this->assertTrue($client->exists('foo'));
$this->assertTrue($client->exists('hoge'));
}
function testCommandPipeline_Flush() {
$client = RC::getConnection();
$client->flushdb();
$pipe = $client->pipeline();
$pipe->set('foo', 'bar')->set('hoge', 'piyo');
$pipe->flushPipeline();
$pipe->ping()->mget(array('foo', 'hoge'));
$replies = $pipe->execute();
$this->assertType('array', $replies);
$this->assertEquals(4, count($replies));
$this->assertEquals('bar', $replies[3][0]);
$this->assertEquals('piyo', $replies[3][1]);
}
}
?>
+93 -14
View File
@@ -11,6 +11,10 @@ if (!function_exists('array_union')) {
}
}
function p_anon($param, $function) {
return create_function($param, $function);
}
class RC {
const SERVER_HOST = '127.0.0.1';
const SERVER_PORT = 6379;
@@ -21,13 +25,26 @@ class RC {
const EXCEPTION_NO_SUCH_KEY = 'no such key';
const EXCEPTION_OUT_OF_RANGE = 'index out of range';
const EXCEPTION_INVALID_DB_IDX = 'invalid DB index';
const EXCEPTION_VALUE_NOT_INT = 'value is not an integer';
const EXCEPTION_EXEC_NO_MULTI = 'EXEC without MULTI';
const EXCEPTION_SETEX_TTL = 'invalid expire time in SETEX';
const EXCEPTION_HASH_VALNOTINT = 'hash value is not an integer';
private static $_connection;
public static function getConnectionArguments() {
return array('host' => RC::SERVER_HOST, 'port' => RC::SERVER_PORT);
}
public static function getConnectionParameters() {
return new Predis_ConnectionParameters(array('host' => RC::SERVER_HOST, 'port' => RC::SERVER_PORT));
}
private static function createConnection() {
$connection = new Predis\Client(RC::SERVER_HOST, RC::SERVER_PORT);
$serverProfile = Predis_RedisServerProfile::get('dev');
$connection = new Predis_Client(RC::getConnectionArguments(), $serverProfile);
$connection->connect();
$connection->selectDatabase(RC::DEFAULT_DATABASE);
$connection->select(RC::DEFAULT_DATABASE);
return $connection;
}
@@ -45,6 +62,22 @@ class RC {
}
}
public static function helperForBlockingPops($op) {
// TODO: I admit that this helper is kinda lame and it does not run
// in a separate process to properly test BLPOP/BRPOP
$redisUri = sprintf('redis://%s:%d/?database=%d', RC::SERVER_HOST, RC::SERVER_PORT, RC::DEFAULT_DATABASE);
$handle = popen('php', 'w');
fwrite($handle, "<?php
require '../lib/Predis.php';
\$redis = Predis_Client::create('$redisUri');
\$redis->rpush('{$op}1', 'a');
\$redis->rpush('{$op}2', 'b');
\$redis->rpush('{$op}3', 'c');
\$redis->rpush('{$op}1', 'd');
?>");
pclose($handle);
}
public static function getArrayOfNumbers() {
return array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
}
@@ -83,42 +116,88 @@ class RC {
try {
$wrapFunction($testcaseInstance);
}
catch (Predis\ServerException $exception) {
catch (Predis_ServerException $exception) {
$thrownException = $exception;
}
$testcaseInstance->assertType('Predis\ServerException', $thrownException);
$testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
$testcaseInstance->assertType('Predis_ServerException', $thrownException);
if (isset($expectedMessage)) {
$testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
}
}
public static function pushTailAndReturn(Predis\Client $client, $keyName, Array $values, $wipeOut = 0) {
public static function testForClientException($testcaseInstance, $expectedMessage, $wrapFunction) {
$thrownException = null;
try {
$wrapFunction($testcaseInstance);
}
catch (Predis_ClientException $exception) {
$thrownException = $exception;
}
$testcaseInstance->assertType('Predis_ClientException', $thrownException);
if (isset($expectedMessage)) {
$testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
}
}
public static function testForCommunicationException($testcaseInstance, $expectedMessage, $wrapFunction) {
$thrownException = null;
try {
$wrapFunction($testcaseInstance);
}
catch (Predis_CommunicationException $exception) {
$thrownException = $exception;
}
$testcaseInstance->assertType('Predis_CommunicationException', $thrownException);
if (isset($expectedMessage)) {
$testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
}
}
public static function pushTailAndReturn(Predis_Client $client, $keyName, Array $values, $wipeOut = 0) {
if ($wipeOut == true) {
$client->delete($keyName);
$client->del($keyName);
}
foreach ($values as $value) {
$client->pushTail($keyName, $value);
$client->rpush($keyName, $value);
}
return $values;
}
public static function setAddAndReturn(Predis\Client $client, $keyName, Array $values, $wipeOut = 0) {
public static function setAddAndReturn(Predis_Client $client, $keyName, Array $values, $wipeOut = 0) {
if ($wipeOut == true) {
$client->delete($keyName);
$client->del($keyName);
}
foreach ($values as $value) {
$client->setAdd($keyName, $value);
$client->sadd($keyName, $value);
}
return $values;
}
public static function zsetAddAndReturn(Predis\Client $client, $keyName, Array $values, $wipeOut = 0) {
public static function zsetAddAndReturn(Predis_Client $client, $keyName, Array $values, $wipeOut = 0) {
// $values: array(SCORE => VALUE, ...);
if ($wipeOut == true) {
$client->delete($keyName);
$client->del($keyName);
}
foreach ($values as $value => $score) {
$client->zsetAdd($keyName, $score, $value);
$client->zadd($keyName, $score, $value);
}
return $values;
}
public static function getConnectionParametersArgumentsArray() {
return array(
'host' => '10.0.0.1', 'port' => 6380, 'connection_timeout' => 10, 'read_write_timeout' => 30,
'database' => 5, 'password' => 'dbpassword', 'alias' => 'connection_alias'
);
}
public static function getConnectionParametersArgumentsString($arguments = null) {
// TODO: must be improved
$args = $arguments !== null ? $arguments : RC::getConnectionParametersArgumentsArray();
$paramsString = "redis://{$args['host']}:{$args['port']}/";
$paramsString .= "?connection_timeout={$args['connection_timeout']}&read_write_timeout={$args['read_write_timeout']}";
$paramsString .= "&database={$args['database']}&password={$args['password']}&alias={$args['alias']}";
return $paramsString;
}
}
?>
+965 -429
View File
File diff suppressed because it is too large Load Diff