Using PHP's "parse_str()" to parse the query string is slightly more
efficient then our own code especially when the number of fields in
the query string grows, with the additional benefit of supporting
arrays for values when brackets are present in fieldnames.
So after this commit, providing this URI string:
$string = 'tcp://127.0.0.1?metavars[]=foo&metavars[]=hoge';
Is equivalent to providing the following named array:
$array = [
'scheme' => 'tcp',
'host' => '127.0.0.1',
'metavars' => ['foo', 'hoge'],
];
Other improvements are that parsing does not break when the value of a
field contains one or more "=" and empty or incomplete "key=value"
pairs result in an empty string for "key".
List of changes:
- The cluster connection sends an ASKING command before retrying
a command on a different node when Redis returns a -ASK response.
- The cluster connection can be initialized with a partial list of
nodes, the full slots map will be fetched from Redis itself using
the CLUSTER NODES command.
- The slots map can be optionally retrieved from Redis if the server
returns a -MOVE response, otherwise only the interested slot will
be permanently reassigned to the new target node.
- It is possible to specify a set of common parameters applied to
connections created on the fly when not part of the current pool
upon -MOVE and -ASK responses returned by Redis for redirections.
- $cluster->connect() connects to a random connection in the pool
instead of forcing the connect operation on all the connections.
We now have a base test case class for Predis (namely PredisTestCase)
grouping various commonly used utility methods shared by all of the
tests in the suite, greatly improving reusability.
NOTE: Backported from v0.9-dev.
Commands are verified by checking their IDs and optionally arguments.
Passing a command instance is only a shortcut to set the expected ID
and arguments, commands are never compared for identity but always for
equivalence.
By raw we mean that input arguments are not filtered and responses are
not parsed, which means arguments must follow the command signature as
defined by Redis and complex responses are left untouched.
When instantiating an instance of `Predis\Command\RawCommand` you must
pass at least the command ID. You can pass further arguments in the
array or you can just set them later with `RawCommand::setArguments()`
but you cannot modify the command ID once instantiated.
$command = new Predis\Command\RawCommand(['SET', 'foo', 'bar']);
$response = $client->executeCommand($command);
While higher level abstractions built upon `Predis\Client` should just
use commands created by the profile in use, inner parts of the library
might use raw commands to provide certain functionalities making sure
that input and output of commands are always consistent, independent
of the profile.
NOTE: Backported from v0.9.
The main reason behind that code duplication was performance related
as we tried to reduce method calls when possible, even at the cost of
falling into the realm of early optimizations. Apparently we just lose
~400 req/sec on a 21000 req/sec basis ("SET foo bar") using PHP 5.5.3
(packaged by Ubuntu 13.10) on an Intel Q6600, so we will most likely
stick with this change for the sake of best practices.
Backported from v0.9-dev.
This iterator tries to mimic the same behaviour of the cursor-based
iterators implemented upon the SCAN family of commands and offering
only limited guarantees on the returned elements, but uses LRANGE to
fetch items from a list incrementally.
Given the IP address we use for this test (the standard IP assigned by
operating systems when unable to get one lease from a DHCP server), on
certain kernels or network stacks we can get different messages such
as "No route to host". Since we just need to make sure that our test
does not take more than 1 seconds abiding by the sub-second timeout,
we can just live with that.
We are experimenting with a new approach at naming classes using less
redundant names by leveraging the containing namespace. The PHP "use"
directive is not limited to class names but can be used to import the
whole namespace, which means you can do something like this:
use Predis\Collection\Iterator;
// ...
foreach (new Iterator\Keyspace($client) as $key) {
// ...
}
Alternatively you can always rely on "use ... as ..." to import one of
the classes by giving it a more meaningful name in the context of the
root namespace:
use Predis\Collection\Iterator\Keyspace as KeyspaceIterator;
// ...
foreach (new KeyspaceIterator($client) as $key) {
// ...
}
In this specific case we chose to apply the -Key postfix to classes
iterating Redis keys to be more explicit about the fact that those
iterators does not work on local in-memory collections, but fetch
items from a key stored on a remote Redis server.
This iterator allows to perform full iterations over fields and values of a
hash by wrapping the incremental nature of HSCAN just like we did for SCAN:
$client = new Predis\Client('tcp://127.0.0.1', ['profile' => '2.8']);
$iterator = new Predis\Iterator\Scan\HashIterator($client, "hash_key");
foreach ($iterator as $field => $value) {
echo "$field => $value" . PHP_EOL;
}
Being HSCAN closely related to SCAN, it is subject to the same behaviour,
see http://redis.io/commands/scan for reference.
This iterator allows to perform full iterations over the members of a sorted
set by wrapping the incremental nature of ZSCAN just like we did for SCAN:
$client = new Predis\Client('tcp://127.0.0.1', ['profile' => '2.8']);
$iterator = new Predis\Iterator\Scan\SortedSetIterator($client, "zset_key");
foreach ($iterator as $member => $rank) {
echo "$rank => $member" . PHP_EOL;
}
Being ZSCAN closely related to SCAN, it is subject to the same behaviour,
see http://redis.io/commands/scan for reference.
This iterator implementation returns the member as key and the rank as value
since the rank is a float value which would be truncated when transforming
the iteration to an array (e.g. using iterator_to_array()). Luckily PHP
preserves the insertion order for named arrays members still result sorted.