This commit is a complete rewrite of the classes previously contained
in the Predis\Option namespace aimed at lowering the initialization
overhead while bringing in more consistency. The overall idea is still
the same with a mini DI container, Predis\Configuration\Options, which
carries options with values that can be initialized lazily.
The first difference with our previous implementation is that now even
user-defined options can be initialized lazily, everything needed is
an object responding to the __invoke() magic method such as a closure.
Other kind of callable arguments (strings, arrays) will be treated as
plain values. The only drawback is that we cannot pass any instance of
classes implementing __invoke() as an option value, but considered the
limited scope of our use case we can say it's more of an acceptable
compromise. Callbacks used for lazy initialization will receive two
arguments upon invokation:
- The current instance of Predis\Configuration\Option ($options)
- A string containing the name of the option ($option)
This is an example in actual code:
$options = new Predis\Configuration\Options([
'exceptions' => true,
'profile' => '2.8',
'distributor' => function () {
return new Predis\Cluster\Distribution\KetamaPureRing();
},
'cluster' => function ($options) {
$distr = $options->distributor;
$strategy = new Predis\Cluster\PredisClusterHashStrategy($distr);
$cluster = new Predis\Connection\PredisCluster();
return $cluster;
},
'connections' => function ($options, $option) {
$factory = $options->getDefault($option);
$factory->define('tcp', 'Predis\Connection\PhpiredisConnection');
return $factory;
},
]);
As you can see there's very little difference compared to before in
the actual usage as most changes are under the hood. Some options such
as "exceptions" and "replication" can now correctly parse bool values
from strings (so the string "false" is not evaluated as boolean true).
While options were initially conceived to configure the client and its
behavior, the concept has matured and it's perfectly fine to consider
the use of Predis\Configuration\Options to propagate configurations to
inner parts of the library.
This commit marks the start of works for the next major release of
Predis which will bring various breaking changes needed to polish the
internal design making the library even more flexible to use or extend
and, more importantly, almost stable in terms of API.
The Redis commands API exposed by Predis\Client is not going to change
much if not at all which is a good news. The most immediate changes
affecting developers will involve the renaming of a few namespaces and
classes, the removal of some previously deprecated classes and methods
and some tweaks to the current abstractions.
Right now the plan is to have a fast paced development to release this
version as soon as possible, ideally a few weeks later than Redis 2.8,
then wait to see the final definition of redis-cluster so that we can
tweak our code if needed and finally hit the v1.0.0 milestone with as
few changes as possible. Furthermore, v1.x will most likely be the
last version of Predis supporting PHP 5.3 as we will start migrating
to PHP 5.4 (or even 5.5) with v2.x, which is not going to happen soon
anyway.
Client::pubSub() still works like usual by returning a new pub/sub
context, but it is now considered an alias of Client::pubSubLoop().
This change is necessary in preparation for the next major version
of Predis where Client::pubSub() will be used for the new PUBSUB
command introduced in Redis 2.8.
Previously the getClientFor() method in a subclass of Predis\Client
returned an instance of Predis\Client instead of a new instance of
the subclass. The new behaviour is more correct.
Connection classes should just handle, convert and return simple Redis
types while parsing and transforming structured replies should be done
by consumers (see Predis\Client or Predis\Transaction\MultiExecContext).
This actually makes more sense considering that parsing a complex response
with the associated command parser may require different actions. As an
example, the result of EXEC is a multibulk that holds the actual responses,
so we really need to parse each one of its elements and we should also
make sure that iterable multibulks are consumed. We already did that
previously, but it was weird knowing that command parsers were applied
by the connection class.
This also moves some duplicated logic away from each connection class
implementation which is a nice bonus.
When no server profile is specified, the connection factory will not push
any initialization command to the newly created connection object.
This change is mainly useful when using redis-cluster and also makes it
possible to easily inject commands such as "SCRIPT LOAD" at initialization
time by grouping everything into one common place simply by extending the
connection factory class.
Now Predis\Command\ScriptedCommand uses EVALSHA instead of EVAL internally
so that performances should be better since the client do not resend the
Lua script body on each call.
Plain EVALSHA commands are not affected and will return or throw the error.
The "throw_errors" connection parameter has been removed and replaced by the
new "exceptions" client option since exceptions on -ERR replies returned by
Redis are not generated by connection classes anymore but are thrown by the
client class and other abstractions such as pipeline contexts.
This change does not affect much people using the Predis\Client class (aside
from the different configuration) but gives much more flexibility to those
building their own pieces of code around the internal classes of Predis.
Now developers can pass a client object or an executable context (see
Predis\Transaction\MultiExecContext or Predis\Pipeline\PipelineContext)
interchangeably as parameters to their methods using the new interface
Predis\BasicCliantInterface.
These new interfaces will also allow us to easily create new client
classes aside from the standard Predis\Client one.
The reason for this change is that not every cluster implementation can support
this behaviour, think of Redis cluster for example. We moved the implementation
of this method in Predis\Connection\PredisCluster since it can still be useful.
Previously it was possible to create a new instance of Predis\Client using
the alias of a single connection in a cluster of connections. Now we added
the ability to do this also when using master/slave replication.
When the "replication" option is specified and evaluates to true, the array of
connection parameters passed to the client constructor os be used to initialize
a new instance of Predis\Network\IConnectionReplication. This option can also
accept a callable object used as an initializer or the fully-qualified name of
a class.