Compare commits

...

27 Commits

Author SHA1 Message Date
Daniele Alessandri 897c217fe3 Backported changes from the mainline library to the PHP 5.2 branch (up to commit ca422b0) 2011-02-12 14:03:58 +01:00
Daniele Alessandri c5d29a8ca4 Backported changes from the mainline library to the PHP 5.2 branch (up to commit 118af28) 2011-01-26 15:24:19 +01:00
Daniele Alessandri 3f058cb5ea Backported changes from the mainline library to the PHP 5.2 branch (up to commit ec42796) 2011-01-01 18:53:59 +01:00
Daniele Alessandri f2ab80e7e9 Add missing example for CAS with MULTI/EXEC abstraction. 2011-01-01 17:40:35 +01:00
Daniele Alessandri ddb5109805 Backported changes from the mainline library to the PHP 5.2 branch (up to commit c75bdd9) 2011-01-01 17:18:33 +01:00
Daniele Alessandri ccb48a1857 Backported changes from the mainline library to the PHP 5.2 branch (up to commit 7db5e65) 2010-11-28 20:25:36 +01:00
Daniele Alessandri 2fc80a5f0d Backported changes from the mainline library to the PHP 5.2 branch (up to commit 975bd98) 2010-11-28 19:25:40 +01:00
Daniele Alessandri 49e151a3d0 Backported changes from the mainline library to the PHP 5.2 branch (up to commit bd961a7) 2010-07-11 17:57:26 +02:00
Daniele Alessandri 7d181160a3 Backported changes from the mainline library to the PHP 5.2 branch (up to commit f073bc8) 2010-07-01 17:10:52 +02:00
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
17 changed files with 5402 additions and 1064 deletions
+207
View File
@@ -0,0 +1,207 @@
v0.6.4 (2011-02-12)
* Various performance improvements (15% ~ 25%) especially when dealing with
long multibulk replies or when using clustered connections.
* Added the "on_retry" option to Predis_MultiExecBlock that can be used to
specify an external callback (or any callable object) that gets invoked
whenever a transaction is aborted by the server.
* Added inline (p)subscribtion via options when initializing an instance of
Predis_PubSubContext.
v0.6.3 (2011-01-01)
* New commands available in the Redis v2.2 profile (dev):
- Strings: SETRANGE, GETRANGE, SETBIT, GETBIT
- Lists : BRPOPLPUSH
* The abstraction for MULTI/EXEC transactions has been dramatically improved
by providing support for check-and-set (CAS) operations when using Redis >=
2.2. Aborted transactions can also be optionally replayed in automatic up
to a user-defined number of times, after which a Predis_AbortedMultiExec
exception is thrown.
v0.6.2 (2010-11-28)
* Minor internal improvements and clean ups.
* New commands available in the Redis v2.2 profile (dev):
- Strings: STRLEN
- Lists : LINSERT, RPUSHX, LPUSHX
- ZSets : ZREVRANGEBYSCORE
- Misc. : PERSIST
* WATCH also accepts a single array parameter with the keys that should be
monitored during a transaction.
* Improved the behaviour of Predis_MultiExecBlock in certain corner cases.
* Improved parameters checking for the SORT command.
* FIX: the STORE parameter for the SORT command didn't work correctly when
using '0' as the target key (ISSUE #13).
* FIX: the methods for UNWATCH and DISCARD do not break anymore method
chaining with Predis\MultiExecBlock.
v0.6.1 (2010-07-11)
* Minor internal improvements and clean ups.
* New commands available in the Redis v2.2 profile (dev):
- Misc. : WATCH, UNWATCH
* Optional modifiers for ZRANGE, ZREVRANGE and ZRANGEBYSCORE queries are
supported using an associative array passed as the last argument of their
respective methods.
* The LIMIT modifier for ZRANGEBYSCORE can be specified using either:
- an indexed array: array($offset, $count)
- an associative array: array('offset' => $offset, 'count' => $count)
* The method Predis_Client::__construct() now accepts also instances of
Predis_ConnectionParameters.
* Predis_MultiExecBlock and Predis_PubSubContext now throw an exception
when trying to create their instances using a profile that does not
support the required Redis commands or when the client is connected to
a cluster of connections.
* Various improvements to Predis_MultiExecBlock:
- fixes and more consistent behaviour across various usage cases.
- support for WATCH and UNWATCH when using the current development
profile (Redis v2.2) and aborted transactions.
* New signature for Predis_Client::multiExec() which is now able to accept
an array of options for the underlying instance of Predis_MultiExecBlock.
Backwards compatibility with previous releases of Predis is ensured.
* New signature for Predis_Client::pipeline() which is now able to accept
an array of options for the underlying instance of Predis_CommandPipeline.
Backwards compatibility with previous releases of Predis is ensured.
The method Predis_Client::pipelineSafe() is to be considered deprecated.
* FIX: The WEIGHT modifier for ZUNIONSTORE and ZINTERSTORE was handled
incorrectly with more than two weights specified.
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.
+42 -30
View File
@@ -3,32 +3,39 @@
## 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 and 2.2. 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).
- Abstraction for Redis transactions (>= 2.0) with support for CAS operations (>= 2.2).
- 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 +45,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 +61,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 = new Predis_Client(array(
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 +83,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 +107,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 +133,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.4
+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']
);
?>
+38
View File
@@ -0,0 +1,38 @@
<?php
require_once 'SharedConfigurations.php';
/*
This is an implementation of an atomic client-side ZPOP using the support for
check-and-set (CAS) operations with MULTI/EXEC transactions, as described in
"WATCH explained" from http://redis.io/topics/transactions
First, populate your database with a tiny sample data set:
./redis-cli
SELECT 15
ZADD zset 1 a
ZADD zset 2 b
ZADD zset 3 c
*/
function zpop($client, $zsetKey) {
$element = null;
$options = array(
'cas' => true, // Initialize with support for CAS operations
'watch' => $zsetKey, // Key that needs to be WATCHed to detect changes
);
$tx = $client->multiExec($options);
@list($element) = $tx->zrange($zsetKey, 0, 0);
if (isset($element)) {
$tx->multi(); // With CAS, MULTI *must* be explicitly invoked.
$tx->zrem($zsetKey, $element);
$tx->exec();
}
return $element;
}
$redis = new Predis_Client($single_server, 'dev');
$zpopped = zpop($redis, 'zset');
echo isset($zpopped) ? "ZPOPed $zpopped" : "Nothing to ZPOP!", "\n";
?>
+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
*/
?>
?>
+2397 -551
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;
}
}
?>
+744
View File
@@ -0,0 +1,744 @@
<?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->skipParse);
$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->skipParse);
$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(new Predis_ConnectionParameters(
RC::getConnectionArguments() + array('read_write_timeout' => 0.5)
));
$connection->connect();
$this->assertTrue($connection->isConnected());
$connection->writeCommand($cmd);
$connection->disconnect();
$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());
}
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("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n");
$rawCmdUnexpected = "*3\r\n$5\r\nLPUSH\r\n$3\r\nkey\r\n$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]);
}
/* Client + MultiExecBlock */
function testMultiExecBlock_Simple() {
$client = RC::getConnection();
$client->flushdb();
$multi = $client->multiExec();
$this->assertType('Predis_MultiExecBlock', $multi);
$this->assertType('Predis_MultiExecBlock', $multi->set('foo', 'bar'));
$this->assertType('Predis_MultiExecBlock', $multi->set('hoge', 'piyo'));
$this->assertType('Predis_MultiExecBlock', $multi->mset(array(
'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
)));
$this->assertType('Predis_MultiExecBlock', $multi->mget(array(
'foo', 'hoge', 'foofoo', 'hogehoge'
)));
$replies = $multi->execute();
$this->assertType('array', $replies);
$this->assertEquals(4, count($replies));
$this->assertEquals(4, count($replies[3]));
$this->assertEquals('barbar', $replies[3][2]);
}
function testMultiExecBlock_FluentInterface() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->multiExec()->ping()->set('foo', 'bar')->get('foo')->execute();
$this->assertType('array', $replies);
$this->assertEquals('bar', $replies[2]);
}
function testMultiExecBlock_CallableAnonymousBlock() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->multiExec(p_anon("\$multi", "
\$multi->ping();
\$multi->set('foo', 'bar');
\$multi->get('foo');
"));
$this->assertType('array', $replies);
$this->assertEquals('bar', $replies[2]);
}
/**
* @expectedException Predis_ClientException
*/
function testMultiExecBlock_CannotMixFluentInterfaceAndAnonymousBlock() {
$emptyBlock = p_anon("\$tx", "");
$tx = RC::getConnection()->multiExec()->get('foo')->execute($emptyBlock);
}
function testMultiExecBlock_EmptyCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->multiExec(p_anon("\$multi", ""));
$this->assertEquals(0, count($replies));
$options = array('cas' => true);
$replies = $client->multiExec($options, p_anon("\$multi", ""));
$this->assertEquals(0, count($replies));
$options = array('cas' => true);
$replies = $client->multiExec($options, p_anon("\$multi", "
\$multi->multi();
"));
$this->assertEquals(0, count($replies));
}
function testMultiExecBlock_ClientExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$expectedMessage = 'TEST';
$thrownException = null;
try {
$client->multiExec(p_anon("\$multi", "
\$multi->ping();
\$multi->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 testMultiExecBlock_ServerExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$client->getResponseReader()->setHandler('-', new Predis_ResponseErrorSilentHandler());
$multi = $client->multiExec();
$multi->set('foo', 'bar');
$multi->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
$multi->set('hoge', 'piyo');
$replies = $multi->execute();
$this->assertType('array', $replies);
$this->assertType('Predis_ResponseError', $replies[1]);
$this->assertTrue($client->exists('foo'));
$this->assertTrue($client->exists('hoge'));
}
function testMultiExecBlock_Discard() {
$client = RC::getConnection();
$client->flushdb();
$multi = $client->multiExec();
$multi->set('foo', 'bar');
$multi->discard();
$multi->set('hoge', 'piyo');
$replies = $multi->execute();
$this->assertEquals(1, count($replies));
$this->assertFalse($client->exists('foo'));
$this->assertTrue($client->exists('hoge'));
}
function testMultiExecBlock_DiscardEmpty() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->multiExec()->discard()->execute();
$this->assertEquals(0, count($replies));
}
function testMultiExecBlock_Watch() {
$client1 = RC::getConnection();
$client2 = RC::getConnection(true);
$client1->flushdb();
$thrownException = null;
try {
$multi = $client1->multiExec(array('watch' => 'sentinel'));
$multi->set('sentinel', 'client1');
$multi->get('sentinel');
$client2->set('sentinel', 'client2');
$multi->execute();
}
catch (PredisException $exception) {
$thrownException = $exception;
}
$this->assertType('Predis_AbortedMultiExec', $thrownException);
$this->assertEquals('The current transaction has been aborted by the server', $thrownException->getMessage());
$this->assertEquals('client2', $client1->get('sentinel'));
}
function testMultiExecBlock_CheckAndSet() {
$client = RC::getConnection();
$client->flushdb();
$client->set('foo', 'bar');
$options = array('watch' => 'foo', 'cas' => true);
$replies = $client->multiExec($options, p_anon("\$tx", "
\$tx->watch('foobar');
\$foo = \$tx->get('foo');
\$tx->multi();
\$tx->set('foobar', \$foo);
\$tx->mget('foo', 'foobar');
"));
$this->assertType('array', $replies);
$this->assertEquals(array(true, array('bar', 'bar')), $replies);
$tx = $client->multiExec($options);
$tx->watch('foobar');
$foo = $tx->get('foo');
$replies = $tx->multi()
->set('foobar', $foo)
->mget('foo', 'foobar')
->execute();
$this->assertType('array', $replies);
$this->assertEquals(array(true, array('bar', 'bar')), $replies);
}
function testMultiExecBlock_RetryOnServerAbort() {
$client1 = RC::getConnection();
$client1->flushdb();
$retry = 3;
$thrownException = null;
try {
$options = array('watch' => 'sentinel', 'retry' => $retry);
$client1->multiExec($options, p_anon("\$tx", "
\$tx->set('sentinel', 'client1');
\$tx->get('sentinel');
\$client2 = RC::getConnection(true);
\$client2->incr('attempts');
\$client2->set('sentinel', 'client2');
"));
}
catch (Predis_AbortedMultiExec $exception) {
$thrownException = $exception;
}
$this->assertType('Predis_AbortedMultiExec', $thrownException);
$this->assertEquals('The current transaction has been aborted by the server', $thrownException->getMessage());
$this->assertEquals('client2', $client1->get('sentinel'));
$this->assertEquals($retry + 1, $client1->get('attempts'));
$client1->del('attempts', 'sentinel');
$thrownException = null;
try {
$options = array(
'watch' => 'sentinel',
'cas' => true,
'retry' => $retry
);
$client1->multiExec($options, p_anon("\$tx", "
\$tx->incr('attempts');
\$tx->multi();
\$tx->set('sentinel', 'client1');
\$tx->get('sentinel');
\$client2 = RC::getConnection(true);
\$client2->set('sentinel', 'client2');
"));
}
catch (Predis_AbortedMultiExec $exception) {
$thrownException = $exception;
}
$this->assertType('Predis_AbortedMultiExec', $thrownException);
$this->assertEquals('The current transaction has been aborted by the server', $thrownException->getMessage());
$this->assertEquals('client2', $client1->get('sentinel'));
$this->assertEquals($retry + 1, $client1->get('attempts'));
}
}
?>
+100 -15
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;
@@ -20,18 +24,37 @@ class RC {
const EXCEPTION_WRONG_TYPE = 'Operation against a key holding the wrong kind of value';
const EXCEPTION_NO_SUCH_KEY = 'no such key';
const EXCEPTION_OUT_OF_RANGE = 'index out of range';
const EXCEPTION_OFFSET_RANGE = 'offset is 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';
const EXCEPTION_BIT_VALUE = 'bit is not an integer or out of range';
const EXCEPTION_BIT_OFFSET = 'bit offset is not an integer or out of range';
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;
}
public static function getConnection() {
public static function getConnection($new = false) {
if ($new == true) {
return self::createConnection();
}
if (self::$_connection === null || !self::$_connection->isConnected()) {
self::$_connection = self::createConnection();
}
@@ -45,6 +68,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 +122,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;
}
}
?>
+1217 -429
View File
File diff suppressed because it is too large Load Diff