When sending raw commands their arguments are not filtered, responses
are not parsed and key prefixes are not applied. The client also does
not throw any exception on Redis errors regardless of its settings.
The first parameter takes the raw arguments of the command (included
its identifier) as defined by the Redis documentation while the second
optional parameter is always populated by reference to indicate when
Redis actually returned an error response.
$client->raw(['PING']); // "PONG"
$client->raw(['SET','foo','bar']); // "OK"
$client->raw(['GET','foo'], $err); // "bar", $err=FALSE
$client->raw(['LPUSH','foo',1], $err); // "WRONGTYPE...", $err=TRUE
Internally, this method creates instances of Predis\Command\RawCommand
that get passed to the underlying connection instance for execution as
if they were usual commands defined by Predis.
Raw commands work in both cluster and replication scenarios since they
are recognized by their command ID, but key prefixing is not supported
since it is done by the profile instance when instantiating commands.
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.
We also changed our wording to indentify this kind of abstraction so
instead of using "scripted commands" (kind of broken English) we now
use "scriptable commands".
It was too broad in concept and not really that useful, so for now we
assume that only our base Predis\Profile\RedisProfile class can handle
command processing and we will decide later if we want to add a couple
of more methods to Predis\Profile\ProfileInterface.
While command classes define how the client should filter arguments or
parse responses, key prefixing depends on the actual command signature
as defined by Redis so it really is something that should be handled
separately as the norm.
Developers can define new handlers or override existing ones, but they
can still define the key prefixing logic inside their command classes
by implementing Predis\Command\PrefixableCommandInterface: the key
prefix processor will just use that by overriding any defined handler.
In this method we can put the logic needed to prepare the connection
right before sending the queued commands to the server (e.g. switching
to the master server when connected in replication mode).
Commands such as MULTI, EXEC, DISCARD, WATCH and UNWATCH disregard the
the "exceptions" client option and always result in an exception being
thrown on error responses even when the client is configured to return
those errors ("exceptions" set to FALSE).
We also changed some options for this class, the accepted ones are:
- "keys": string or array of strings for automatic WATCH.
- "cas": sets the check-and-set mode.
- "retry": number of attempts before giving up aborted transactions.
- "exceptions": sets whether exceptions should be thrown on error
responses (overrides the "exceptions" client option).
The "on_retry" option has been removed.
The Predis\Transaction\MultiExecState class is actually quite dummy as
it does not work as a finite-state machine, but is used to track state
using flags. It basically replicates the same behaviour of when it was
part of the Predis\Transaction\MultiExec code and it will do for now.
Only two options available for now, used to specify which kind of
pipeline object the client should use or return:
- "atomic": returns a pipeline wrapped in a MULTI / EXEC transaction
(class: Predis\Pipeline\Atomic).
- "fire-and-forget": returns a pipeline that does not read back
responses from the server (class: Predis\Pipeline\FireAndForget).
We might add more options in the future.
First of all we completely removed the concept of pipeline executors.
Now pipelines can be easily customized by extending our default class
Predis\Pipeline\Pipeline.
Tests coverage for the Predis\Pipeline namespace is decent but can be
definitely improved while test cases can be beautified.
This option must return a callable object that is used to override how
the client aggregates connections when passing an array of parameters
to its constructor.
When specified, this option overrides both "cluster" and "replication"
as it allows to make use of your own code to aggregate multiple nodes.
This is, for example, how you can mimic the standard initialization of
a cluster that relies on client-side sharding:
$parameters = ['tcp://127.0.0.1:6380', 'tcp://127.0.0.1:6381'];
$options = [
'aggregate' => function () {
return function ($parameters, $options) {
$connection = new Predis\Connection\PredisCluster();
$options->connections->aggregate($connection, $parameters);
return $connection;
};
},
];
$client = new Predis\Client($parameters, $options);
When invoked by the client, the specified callable must always return
a Predis\Connection\ConnectionInterface instance or the client will
throw an UnexpectedValueException.
Using list() with the warning suppressor is slower than using isset()
to check the presence of the first two elements of the array returned
by explode(). This also allow us to skip incomplete query string pairs
when parsing the URI string.
We have also changed the exception being thrown on invalid URIs to a
more appropriate one.
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.
The "profile" member is actually used for caching purposes as fetching
its value from the options instance would add noticeable overhead in a
part of the client where every bit of optimization matters, for this
we decided to keep it private.