Compare commits

..

11 Commits

Author SHA1 Message Date
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 1108 additions and 5042 deletions
-207
View File
@@ -1,207 +0,0 @@
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,5 +1,5 @@
Copyright (c) 2009-2011 Daniele Alessandri
Copyright (c) 2009-2010 Daniele Alessandri
qq
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
+28 -31
View File
@@ -3,7 +3,9 @@
## About ##
Predis is a flexible and feature-complete PHP client library for the Redis key-value
database. It currently comes in two flavors:
database.
Predis is currently a work-in-progress and it comes in two flavors:
- 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.
@@ -15,14 +17,12 @@ Please refer to the TODO file to see which issues are still pending and what is
to be implemented soon in Predis.
## Main features ##
## Features ##
- 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.
- 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
## Quick examples ##
@@ -35,7 +35,7 @@ complete coverage of all the features available in Predis.
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');
@@ -45,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('redis://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) ##
@@ -61,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 = new Predis\Client(array(
$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 ###
@@ -83,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\MultiBulkCommand {
class BrandNewRedisCommand extends Predis_InlineCommand {
public function getCommandId() { return 'NEWCMD'; }
}
$redis = new Predis\Client();
$redis->getProfile()->registerCommand('BrandNewRedisCommand', 'newcmd');
$redis = new Predis_Client();
$redis->registerCommand('BrandNewRedisCommand', 'newcmd');
$redis->newcmd();
@@ -107,7 +107,7 @@ 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 | E_STRICT.
variable set to E_ALL.
## Dependencies ##
@@ -133,9 +133,6 @@ variable set to E_ALL | E_STRICT.
[Daniele Alessandri](mailto:suppakilla@gmail.com)
## Contributors ##
[Lorenzo Castelli](http://github.com/lcastelli)
## License ##
+3 -4
View File
@@ -1,7 +1,6 @@
* Documentation! The README is obviously not enought to show how to use
Predis as it does not cover all of its features.
* Add more tests targeting specific functions of the library.
* Missing tests for commands:
PUBLISH, SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE
* 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.
-1
View File
@@ -1 +0,0 @@
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 = new Predis\Client($single_server);
$redis = Predis_Client::create($configurations);
$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
)
*/
?>
?>
-60
View File
@@ -1,60 +0,0 @@
<?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++;
}
public function remove($node) {
$this->_nodes = array_filter($this->_nodes, function($n) use($node) {
return $n !== $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']
);
?>
-41
View File
@@ -1,41 +0,0 @@
<?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
'retry' => 3, // Number of retries on aborted transactions, after
// which the client bails out with an exception.
);
$txReply = $client->multiExec($options, function($tx)
use ($zsetKey, &$element) {
@list($element) = $tx->zrange($zsetKey, 0, 0);
if (isset($element)) {
$tx->multi(); // With CAS, MULTI *must* be explicitly invoked.
$tx->zrem($zsetKey, $element);
}
});
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 = new Predis\Client($single_server);
$redis = Predis_Client::create($configurations);
$redis->mset($mkv);
$retval = $redis->mget(array_keys($mkv));
@@ -26,4 +26,4 @@ Array
[2] => Third user
)
*/
?>
?>
-52
View File
@@ -1,52 +0,0 @@
<?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");
?>
+2 -17
View File
@@ -1,24 +1,9 @@
<?php
require_once '../lib/Predis.php';
$single_server = array(
$configurations = 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 = new Predis\Client($single_server);
$redis = Predis_Client::create($configurations);
$redis->set('library', 'predis');
$retval = $redis->get('library');
@@ -13,4 +13,4 @@ print_r($retval);
/* OUTPUT
predis
*/
?>
?>
+584 -2120
View File
File diff suppressed because it is too large Load Diff
-500
View File
@@ -1,500 +0,0 @@
<?php
namespace Predis;
RedisServerProfile::registerProfile('\Predis\RedisServer_v1_0', '1.0');
class 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',
);
}
}
/* ------------------------------------------------------------------------- */
namespace Predis\Compatibility\v1_0\Commands;
/* miscellaneous commands */
class Ping extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'PING'; }
public function parseResponse($data) {
return $data === 'PONG' ? true : false;
}
}
class DoEcho extends \Predis\BulkCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'ECHO'; }
}
class Auth extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'AUTH'; }
}
/* connection handling */
class 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 Set extends \Predis\BulkCommand {
public function getCommandId() { return 'SET'; }
}
class SetPreserve extends \Predis\BulkCommand {
public function getCommandId() { return 'SETNX'; }
public function parseResponse($data) { return (bool) $data; }
}
class Get extends \Predis\InlineCommand {
public function getCommandId() { return 'GET'; }
}
class GetMultiple extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'MGET'; }
}
class GetSet extends \Predis\BulkCommand {
public function getCommandId() { return 'GETSET'; }
}
class Increment extends \Predis\InlineCommand {
public function getCommandId() { return 'INCR'; }
}
class IncrementBy extends \Predis\InlineCommand {
public function getCommandId() { return 'INCRBY'; }
}
class Decrement extends \Predis\InlineCommand {
public function getCommandId() { return 'DECR'; }
}
class DecrementBy extends \Predis\InlineCommand {
public function getCommandId() { return 'DECRBY'; }
}
class Exists extends \Predis\InlineCommand {
public function getCommandId() { return 'EXISTS'; }
public function parseResponse($data) { return (bool) $data; }
}
class Delete extends \Predis\InlineCommand {
public function getCommandId() { return 'DEL'; }
}
class Type extends \Predis\InlineCommand {
public function getCommandId() { return 'TYPE'; }
}
/* commands operating on the key space */
class 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 RandomKey extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'RANDOMKEY'; }
public function parseResponse($data) { return $data !== '' ? $data : null; }
}
class Rename extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'RENAME'; }
}
class RenamePreserve extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'RENAMENX'; }
public function parseResponse($data) { return (bool) $data; }
}
class Expire extends \Predis\InlineCommand {
public function getCommandId() { return 'EXPIRE'; }
public function parseResponse($data) { return (bool) $data; }
}
class ExpireAt extends \Predis\InlineCommand {
public function getCommandId() { return 'EXPIREAT'; }
public function parseResponse($data) { return (bool) $data; }
}
class DatabaseSize extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'DBSIZE'; }
}
class TimeToLive extends \Predis\InlineCommand {
public function getCommandId() { return 'TTL'; }
}
/* commands operating on lists */
class ListPushTail extends \Predis\BulkCommand {
public function getCommandId() { return 'RPUSH'; }
}
class ListPushHead extends \Predis\BulkCommand {
public function getCommandId() { return 'LPUSH'; }
}
class ListLength extends \Predis\InlineCommand {
public function getCommandId() { return 'LLEN'; }
}
class ListRange extends \Predis\InlineCommand {
public function getCommandId() { return 'LRANGE'; }
}
class ListTrim extends \Predis\InlineCommand {
public function getCommandId() { return 'LTRIM'; }
}
class ListIndex extends \Predis\InlineCommand {
public function getCommandId() { return 'LINDEX'; }
}
class ListSet extends \Predis\BulkCommand {
public function getCommandId() { return 'LSET'; }
}
class ListRemove extends \Predis\BulkCommand {
public function getCommandId() { return 'LREM'; }
}
class ListPopFirst extends \Predis\InlineCommand {
public function getCommandId() { return 'LPOP'; }
}
class ListPopLast extends \Predis\InlineCommand {
public function getCommandId() { return 'RPOP'; }
}
/* commands operating on sets */
class SetAdd extends \Predis\BulkCommand {
public function getCommandId() { return 'SADD'; }
public function parseResponse($data) { return (bool) $data; }
}
class SetRemove extends \Predis\BulkCommand {
public function getCommandId() { return 'SREM'; }
public function parseResponse($data) { return (bool) $data; }
}
class SetPop extends \Predis\InlineCommand {
public function getCommandId() { return 'SPOP'; }
}
class SetMove extends \Predis\BulkCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SMOVE'; }
public function parseResponse($data) { return (bool) $data; }
}
class SetCardinality extends \Predis\InlineCommand {
public function getCommandId() { return 'SCARD'; }
}
class SetIsMember extends \Predis\BulkCommand {
public function getCommandId() { return 'SISMEMBER'; }
public function parseResponse($data) { return (bool) $data; }
}
class SetIntersection extends \Predis\InlineCommand {
public function getCommandId() { return 'SINTER'; }
}
class SetIntersectionStore extends \Predis\InlineCommand {
public function getCommandId() { return 'SINTERSTORE'; }
}
class SetUnion extends \Predis\InlineCommand {
public function getCommandId() { return 'SUNION'; }
}
class SetUnionStore extends \Predis\InlineCommand {
public function getCommandId() { return 'SUNIONSTORE'; }
}
class SetDifference extends \Predis\InlineCommand {
public function getCommandId() { return 'SDIFF'; }
}
class SetDifferenceStore extends \Predis\InlineCommand {
public function getCommandId() { return 'SDIFFSTORE'; }
}
class SetMembers extends \Predis\InlineCommand {
public function getCommandId() { return 'SMEMBERS'; }
}
class SetRandomMember extends \Predis\InlineCommand {
public function getCommandId() { return 'SRANDMEMBER'; }
}
/* multiple databases handling commands */
class SelectDatabase extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SELECT'; }
}
class MoveKey extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'MOVE'; }
public function parseResponse($data) { return (bool) $data; }
}
class FlushDatabase extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'FLUSHDB'; }
}
class FlushAll extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'FLUSHALL'; }
}
/* sorting */
class 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 Save extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SAVE'; }
}
class 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 LastSave extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'LASTSAVE'; }
}
class Shutdown extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SHUTDOWN'; }
public function closesConnection() { return true; }
}
/* remote server control commands */
class 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 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;
}
}
?>
-755
View File
@@ -1,755 +0,0 @@
<?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());
}
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());
}
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());
}
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());
}
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, function()
use($profile, $bogusCommand) {
$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();
$exceptionMessage = 'Error while reading line from the server';
RC::testForCommunicationException($this, $exceptionMessage, function() use($connection, $cmd) {
$connection->readResponse($cmd);
});
}
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();
RC::testForCommunicationException($this, null, function() use($connection) {
$connection->connect();
});
$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();
RC::testForCommunicationException($this, $expectedMessage, function() use($connection, $cmdFake) {
$connection->readResponse($cmdFake);
});
$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()
);
RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function()
use ($connection, $rawCmdUnexpected) {
$connection->rawCommand($rawCmdUnexpected);
});
}
/* 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(function($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();
RC::testForClientException($this, 'TEST', function() use($client) {
$client->pipeline(function($pipe) {
$pipe->ping();
$pipe->set('foo', 'bar');
throw new \Predis\ClientException("TEST");
});
});
$this->assertFalse($client->exists('foo'));
}
function testCommandPipeline_ServerExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$client->getResponseReader()->setHandler('-', new \Predis\ResponseErrorSilentHandler());
$replies = $client->pipeline(function($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(function($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 = function($tx) { };
$tx = RC::getConnection()->multiExec()->get('foo')->execute($emptyBlock);
}
function testMultiExecBlock_EmptyCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->multiExec(function($multi) { });
$this->assertEquals(0, count($replies));
$options = array('cas' => true);
$replies = $client->multiExec($options, function($multi) { });
$this->assertEquals(0, count($replies));
$options = array('cas' => true);
$replies = $client->multiExec($options, function($multi) {
$multi->multi();
});
$this->assertEquals(0, count($replies));
}
function testMultiExecBlock_ClientExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
RC::testForClientException($this, 'TEST', function() use($client) {
$client->multiExec(function($multi) {
$multi->ping();
$multi->set('foo', 'bar');
throw new \Predis\ClientException("TEST");
});
});
$this->assertFalse($client->exists('foo'));
}
function testMultiExecBlock_ServerExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$client->getResponseReader()->setHandler('-', new \Predis\ResponseErrorSilentHandler());
$replies = $client->multiExec(function($multi) {
$multi->set('foo', 'bar');
$multi->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
$multi->set('hoge', 'piyo');
});
$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();
$replies = $client->multiExec(function($multi) {
$multi->set('foo', 'bar');
$multi->discard();
$multi->set('hoge', 'piyo');
});
$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(function($multi) {
$multi->discard();
});
$this->assertEquals(0, count($replies));
}
function testMultiExecBlock_Watch() {
$client1 = RC::getConnection();
$client2 = RC::getConnection(true);
$client1->flushdb();
RC::testForAbortedMultiExecException($this, function()
use($client1, $client2) {
$client1->multiExec(array('watch' => 'sentinel'), function($multi)
use ($client2) {
$multi->set('sentinel', 'client1');
$multi->get('sentinel');
$client2->set('sentinel', 'client2');
});
});
$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, function($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();
$client2 = RC::getConnection(true);
$client1->flushdb();
$retry = 3;
$attempts = 0;
RC::testForAbortedMultiExecException($this, function()
use($client1, $client2, $retry, &$attempts) {
$options = array('watch' => 'sentinel', 'retry' => $retry);
$client1->multiExec($options, function($tx)
use ($client2, &$attempts) {
$attempts++;
$tx->set('sentinel', 'client1');
$tx->get('sentinel');
$client2->set('sentinel', 'client2');
});
});
$this->assertEquals('client2', $client1->get('sentinel'));
$this->assertEquals($retry + 1, $attempts);
$retry = 3;
$attempts = 0;
RC::testForAbortedMultiExecException($this, function()
use($client1, $client2, $retry, &$attempts) {
$options = array(
'watch' => 'sentinel',
'cas' => true,
'retry' => $retry
);
$client1->multiExec($options, function($tx)
use ($client2, &$attempts) {
$attempts++;
$tx->incr('attempts');
$tx->multi();
$tx->set('sentinel', 'client1');
$tx->get('sentinel');
$client2->set('sentinel', 'client2');
});
});
$this->assertEquals('client2', $client1->get('sentinel'));
$this->assertEquals($retry + 1, $attempts);
$this->assertEquals($attempts, $client1->get('attempts'));
}
/**
* @expectedException InvalidArgumentException
*/
function testMultiExecBlock_RetryNotAvailableWithoutBlock() {
$options = array('watch' => 'foo', 'retry' => 1);
$tx = RC::getConnection()->multiExec($options);
$tx->multi()->get('foo')->exec();
}
function testMultiExecBlock_CheckAndSet_Discard() {
$client = RC::getConnection();
$client->flushdb();
$client->set('foo', 'bar');
$options = array('watch' => 'foo', 'cas' => true);
$replies = $client->multiExec($options, function($tx) {
$tx->watch('foobar');
$foo = $tx->get('foo');
$tx->multi();
$tx->set('foobar', $foo);
$tx->discard();
$tx->mget('foo', 'foobar');
});
$this->assertType('array', $replies);
$this->assertEquals(array(array('bar', null)), $replies);
$hijack = true;
$client->set('foo', 'bar');
$client2 = RC::getConnection(true);
$options = array('watch' => 'foo', 'cas' => true, 'retry' => 1);
$replies = $client->multiExec($options, function($tx)
use ($client2, &$hijack) {
$foo = $tx->get('foo');
$tx->multi();
$tx->set('foobar', $foo);
$tx->discard();
if ($hijack) {
$hijack = false;
$client2->set('foo', 'hijacked!');
}
$tx->mget('foo', 'foobar');
});
$this->assertType('array', $replies);
$this->assertEquals(array(array('hijacked!', null)), $replies);
}
}
?>
+21 -91
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,37 +24,20 @@ 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() {
$serverProfile = Predis\RedisServerProfile::get('dev');
$connection = new Predis\Client(RC::getConnectionArguments(), $serverProfile);
$serverProfile = new Predis_RedisServer_vNext();
$connection = new Predis_Client(array('host' => RC::SERVER_HOST, 'port' => RC::SERVER_PORT), $serverProfile);
$connection->connect();
$connection->select(RC::DEFAULT_DATABASE);
$connection->selectDatabase(RC::DEFAULT_DATABASE);
return $connection;
}
public static function getConnection($new = false) {
if ($new == true) {
return self::createConnection();
}
public static function getConnection() {
if (self::$_connection === null || !self::$_connection->isConnected()) {
self::$_connection = self::createConnection();
}
@@ -71,7 +58,7 @@ class RC {
$handle = popen('php', 'w');
fwrite($handle, "<?php
require '../lib/Predis.php';
\$redis = Predis\Client::create('$redisUri');
\$redis = Predis_Client::create('$redisUri');
\$redis->rpush('{$op}1', 'a');
\$redis->rpush('{$op}2', 'b');
\$redis->rpush('{$op}3', 'c');
@@ -118,99 +105,42 @@ class RC {
try {
$wrapFunction($testcaseInstance);
}
catch (Predis\ServerException $exception) {
catch (Predis_ServerException $exception) {
$thrownException = $exception;
}
$testcaseInstance->assertType('Predis\ServerException', $thrownException);
if (isset($expectedMessage)) {
$testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
}
$testcaseInstance->assertType('Predis_ServerException', $thrownException);
$testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
}
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 testForAbortedMultiExecException($testcaseInstance, $wrapFunction) {
$thrownException = null;
try {
$wrapFunction($testcaseInstance);
}
catch (Predis\AbortedMultiExec $exception) {
$thrownException = $exception;
}
$testcaseInstance->assertType('Predis\AbortedMultiExec', $thrownException);
}
public static function pushTailAndReturn(Predis\Client $client, $keyName, Array $values, $wipeOut = 0) {
public static function pushTailAndReturn(Predis_Client $client, $keyName, Array $values, $wipeOut = 0) {
if ($wipeOut == true) {
$client->del($keyName);
$client->delete($keyName);
}
foreach ($values as $value) {
$client->rpush($keyName, $value);
$client->pushTail($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->del($keyName);
$client->delete($keyName);
}
foreach ($values as $value) {
$client->sadd($keyName, $value);
$client->setAdd($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->del($keyName);
$client->delete($keyName);
}
foreach ($values as $value => $score) {
$client->zadd($keyName, $score, $value);
$client->zsetAdd($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 ?: 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;
}
}
?>
+452 -1145
View File
File diff suppressed because it is too large Load Diff