github predis/predis v1.0.0
Predis v1.0.0

latest releases: v3.0.0-alpha1, v2.2.2, v2.2.1...
7 years ago

This is not only a new and major release of Predis, but it is an important milestone because it represents the first stable release in terms of API (both public and internal). Lots of changes have been made to improve the overall design and code quality while optimizing the library as much as possible.

You can consult the CHANGELOG for more details about changes and bug fixes introduced in this release.

IMPORTANT NOTE: we switched to PSR-4 for autoloading but PEAR packaging still uses PSR-0 for obvious reasons, the conversion is performed when generating the package using bin/create-pear. We do not plan to support installation through PEAR indefinitely, but we will stick with it for a few more releases.

Introduction

One of the purposes of this new release was to make use of better names for classes, interfaces and namespaces so we ended up with shorter fully-qualified names and a better organization of namespaces. While these changes can be quite radical in certain cases, the truth is that they will most likely affect only code-bases where developers made use of certain classes to extend the library with custom features. Those simply using Predis as a client will not probably notice any difference. Just to make a few examples (for the rest, see the CHANGELOG):

  • The Predis\Option namespace is now Predis\Configuration.
  • The Predis\Command\AbstractCommand class is now Predis\Command\Command.
  • The Predis\Command\ScriptedCommand class is now Predis\Command\ScriptCommand.
  • Aggregate connections (cluster, replication) are in the Predis\Connection\Aggregate namespace.
  • Classes representing status and error responses are in the Predis\Response namespace.

Obviously it is not only a matter of renaming or moving things around, the overall internal design of Predis has been dramatically improved and now certain aspects of the library (such as clustering) are more solid and more open to extension. Last but not least a lot of work has been done to keep performances in check, resulting in less overhead when initializing client instances and no difference at runtime in raw numbers compared to v0.8.x despite the additional flexibility.

Improvements

  • @method in docblocks: while this is not strictly a feature, now Predis ships with @method tags for Predis\ClientInterface and Predis\ClientContextInterface (pipelines, transactions) so you can leverage autocompletion and inspection while coding with your favourite IDE. This should be a much welcome addition for many developers since it has been requested many times in the last couple of years.

  • Server profiles: the default one is now 3.0 (obviously targeting Redis 3.0.0). It is safe to switch now since the are no breaking changes between Redis 2.8 and 3.0. At the same time, the server profile for Redis 1.2 has been removed since it is relatively ancient stuff.

  • New commands: added SENTINEL to the server profile for Redis 2.6 and PUBSUB to the server profile for Redis 2.8. Please note that you will not find CLUSTER and other commands used by redis-cluster in the server profile for Redis 3.0. Internally they are used as raw commands, they may be added in the future though as soon as Redis 3.0 becomes stable.

  • Raw commands: now they can be sent simply by providing an array of arguments (comprising of the command ID) to Predis\Client::executeRaw(). This method skips key prefixing (and more generally, any command processor) and response parsing, so it always returns responses as documented on the Redis website. It is also possible to tell when Redis returns an error thanks to the second optional argument populated by reference with a boolean value:

    $response = $client->executeRaw(["SET", "foo", "bar"]); // string(2) "OK"
    $response = $client->executeRaw(["STRLEN", "foo"]);     // int(3)
    
    // When $iserror is TRUE, $response contains the error string.
    $response = $client->executeRaw(["LPUSH", "foo", "bar"], &$iserror);
  • Changes for ZRANGE-like commands using WITHSCORES: Predis used to return the member and score pair as an array of [$member, $score] when using the WITHSCORES modifier with ZRANGE, ZREVRANGE, ZRANGEBYSCORE and ZREVRANGEBYSCORE. Now the raw response is parsed to a named array so members and their scores can be accessed as $member => $score.

    $members = $client->zrange("key", 0, -1, "withscores");
    // Predis v0.8 (old): [["member1", "score1"], ...]
    // Predis v1.0 (new): ["member1" => "score1", ...]

    The output of ZSCAN has also been changed accordingly in order to reflect the same output, while Predis\Collection\Iterator\SortedSetKey is not affected by this change. NOTE: the ordering of the original response is still preserved thanks to how PHP internally works with named arrays.

  • Redis responses: status responses are now returned as instances of Predis\Response\Status and carry their original payload string. +OK is then no more returned to the client as a boolean TRUE, but since a status response can be transparently casted to string one can do $response == "OK" which is also more akin to how Redis replies to clients. Instances of common status responses such as +OK or +QUEUED are shared internally in order to avoid wasting memory. Another change regards custom response parsers in commands (see Predis\Command\CommandInterface::parseResponse()) which are now applied inside consumer classes such as Predis\Client.

  • Client options: the fully reworked Predis\Configuration\Options class now has the ability to lazily initialize values using objects that respond to __invoke() and it works even for custom options defined by the user. This is an example of a complex and modular set of options where standard and user-defined options are mixed together, and the ones regarding cluster are initialized lazily only when needed by the client:

    $client = new Predis\Client($parameters, [
      'exceptions'  => true,
      'connections' => [
        'tcp'  => 'Predis\Connection\PhpiredisStreamConnection',
      ],
      'distributor' => function () {
        return new Predis\Cluster\Distributor\KetamaRing();
      },
      'strategy'    => function ($options) {
        return new Predis\Cluster\PredisStrategy($options->distributor);
      },
      'cluster'     => function ($options) {
        $strategy = $options->strategy;
        $cluster = new Predis\Connection\Aggregate\PredisCluster($strategy);
    
        return $cluster;
      },
      'profile'     => function ($options, $option) {
        $profile = $options->getDefault($option);
        $profile->defineCommand("luascript", "Nrk\Command\LuaScriptCommand");
    
        return $profile;
      },
    ]);

    In addition to the client options already supported by v0.8, the new aggregate option overrides both cluster and replication and can be used to initialize an aggregate connection taking full control over its initialization (the return type must be Predis\Connection\AggregateConnectionInterface).

  • Cluster with client-side sharding: Predis now uses the same rules defined by the redis-cluster specification when dealing with empty hash-tags {} in keys, so upgrading Predis might affect the distribution of your already-deployed cluster. If you want to make sure that you will not be affected by this change, you can extend Predis\Cluster\PredisStrategy to override the extractKeyTag($key) method and configure the client to use your strategy like this:

    class OldPredisStrategy extends Predis\Cluster\PredisStrategy
    {
      protected function extractKeyTag($key)
      {
        if (false !== $start = strpos($key, '{')) {
          if (false !== $end = strpos($key, '}', $start)) {
            $key = substr($key, ++$start, $end - $start);
          }
        }
    
        return $key;
      }
    }
    
    $client = new Predis\Client($nodes, [
      "cluster" => function () {
        $strategy = new OldPredisStrategy();
        $cluster = new Predis\Connection\Aggregate\PredisCluster($strategy);
    
        return $cluster;
      },
    ]);

    This is possible because, starting with v1.0, Predis\Connection\Aggregate\PredisCluster accepts an instance of Predis\Cluster\StrategyInterface as the only argument in its constructor instead of Predis\Cluster\Distributor\DistributorInterface. The distributor, on the other hand, is now wrapped by Predis\Cluster\PredisStrategy.

  • Pipeline options: Predis\Client::pipeline() now accepts options to choose which kind of pipeline object should be initialized among the ones supported by Predis:

    • atomic: wraps the pipeline in a MULTI / EXEC transaction (Predis\Pipeline\Atomic).
    • fire-and-forget: does not read back responses (Predis\Pipeline\FireAndForget).
  • Transaction options: while Predis\Transaction\MultiExec still supports cas, watch and retry, there are also a couple of changes:

    • exceptions: overrides the value of $options->exceptions provided in client options.
    • on_retry: this option has been removed.
  • Key prefixing: while changes in this case are completely transparent to users, the prefixing logic has been moved from command classes to the key prefix processor returned by $options->prefix. Commands are recognized by their IDs and developers can define or override the handlers used to prefix keys based on their arguments. This makes it possible to prefix keys also when using the generic Predis\Command\RawCommand class.

Dropped stuff:

  • Obsolete methods: Predis\Client::multiExec() and Predis\Client::pubSub() have been removed after having been deprecated in v0.8.5. The new names for these methods are respectively Predis\Client::transaction() and Predis\Client::pubSubLoop().
  • Iterable multibulk responses: the commonly used Predis\Connection\StreamConnection does not support them anymore and iterable_multibulk has been removed from the default connection parameters. You can still use them with Predis\Connection\CompositeStreamConnection (it is slower, but makes use of a pluggable protocol system) and the classes implementing multibulk iterators are available in the Predis\Response\Iterator namespace.
  • Pipeline executors: they are no more needed after the changes in Predis\Pipeline\Pipeline.

What's next?

Having finally reached v1.0 is a great milestone considering that Predis has been around for 5 years now, but there is obviously more to come: v1.1 will ship with new features and the most important ones will most likely be support for redis-sentinel with replication and support for slaves in redis-cluster. Minor versions will be released more frequently compared to the past, now that the library is considered stable in terms of design and API.

There is also another aspect that should really be addressed: documentation. Predis simply does not have enough documentation covering useful features or the inner parts of the library. I plan to resume the initial efforts started in the documentation branch but frankly I hope to receive also some external contributions.

All in all I am happy with this release and even though it took 7 months to ship (way more than what I originally planned, mostly due to some busy months) the result is more than satisfactory in terms of quality. Big thanks to everyone who has shared their feedbacks or contributed with suggestions and code!

KTHXBYE.

Downloads

Don't miss a new predis release

NewReleases is sending notifications on new releases.