Compare commits

...

843 Commits

Author SHA1 Message Date
Daniele Alessandri cb18d67b6a Update CHANGELOG and bump VERSION. 2013-07-27 11:13:54 +02:00
Daniele Alessandri 4576dc029f Add LICENSE and phpunit.xml.dist in package.ini for Pear.
This commit addresses issues #124 and #126. LICENSE will be installed
with the "doc" role while phpunit.xml.dist will be installed with the
"test" role.

[ci skip]
2013-06-17 11:16:07 +02:00
Daniele Alessandri aa458a1922 Merge pull request #123 from drealecs/doctype_fixes
some PHPDoc fixes in lib
2013-06-03 03:04:10 -07:00
Alexandru Patranescu 2d2930d24f some PHPDoc fixes in lib
and removed some unused imports also
2013-06-03 00:06:41 +03:00
Daniele Alessandri 7e0ec1a265 Update CHANGELOG.
[ci skip]
2013-05-31 11:52:47 +02:00
Daniele Alessandri 04e597ebba Merge remote-tracking branch 'origin/pr/116' into v0.8 2013-05-31 11:30:30 +02:00
Daniele Alessandri d681bff4ab Merge branch 'github/pr/114' into v0.8 2013-05-31 10:47:58 +02:00
Daniele Alessandri 3dea41aa66 Add tests for prefixed Predis\PubSub\DispatcherLoop. 2013-05-31 10:39:46 +02:00
Daniele Alessandri 69c66e157f Make callbacks properties protected in the DispatcherLoop class.
This makes easier to extend the class. The underlying pub/sub iterator
property is still private, but can be accessed via public getter method.
2013-05-31 10:21:21 +02:00
Daniele Alessandri dd50cfe47e Slightly rework original pull request. 2013-05-31 10:17:54 +02:00
Daniele Alessandri 8451146d38 Expose underlying client object from pub/sub iterators. 2013-05-31 10:17:08 +02:00
Thomas Orozco d45ce35923 Use a random IP when a host has several IPs.
Using gethostbyname, we will reuse the same (first) IP address for
each request. Here, we choose the IP we use randomly.

This is practical in a case where we have multiple redis read-only
slaves that can't invidually support the full application load, but are
accessible through a single hostname.
2013-05-11 14:08:51 -07:00
Eloi Poch 6cd124cc59 Fix DispatcherLoop error with client prefix keys
DispatcherLoop works properly if a client have configured a
prefix for the keys or not
2013-04-20 13:02:17 +02:00
Daniele Alessandri d6fa4a3292 Augment exception message with basic server details on connection error.
Providing a basic hint in the exception message about the server that caused
a connection exception could be useful especially with aggregated connections.

This is in response to issue #110.
2013-03-30 16:16:13 +01:00
Daniele Alessandri 5b771704d3 Handle DUMP and RESTORE with Predis\Connection\PredisCluster. 2013-03-30 14:38:49 +01:00
Daniele Alessandri 68d972a1b1 Merge remote-tracking branch 'origin/pr/111' into v0.8 2013-03-27 15:11:00 +01:00
marcosQuesada 4bf035918d setting medium timeout on slow tests 2013-03-23 00:57:36 +01:00
Daniele Alessandri 94c4e2ffee Fix handling of scripted commands with redis-cluster hash strategy.
A missing "use" directive was preventing the hash strategy to properly
use the specific methods of Predis\Command\ScriptedCommand, falling back
to analyzing the raw arguments array of the command.
2013-03-16 16:24:59 +01:00
Daniele Alessandri 6e2fd181f1 Make sure key prefixing is skipped when command has no arguments.
Actually this was already the case for certain commands, but some of them
was left unguarded for such cases. This commit also fixes #109.

The behaviour of silently skipping key prefixing when a command has no
arguments may change in the future so we added explicit tests as guards
for future changes. Predis\Command\Processor\KeyPrefixProcessor will
continue to skip key prefixing on empty arguments, regardless.
2013-03-16 16:00:33 +01:00
Daniele Alessandri 1c21fcc5b3 Fix strict warnings running command test generator script. 2013-03-16 13:23:46 +01:00
Daniele Alessandri 531261b0fc Add DUMP and RESTORE to the server profile for Redis 2.6.
Closes issue #108.
2013-03-16 13:21:24 +01:00
Daniele Alessandri 7434a9bd0e Add the WITHSCORES modifier only when option value is true.
Fixes issue #107.
2013-03-16 12:37:00 +01:00
Daniele Alessandri c4123ce7ce Update CHANGELOG. 2013-03-16 12:16:52 +01:00
Daniele Alessandri 536992e277 Merge remote-tracking branch 'nitper/cluster_hmset' into v0.8 2013-03-16 12:11:44 +01:00
Daniele Alessandri 5a735aa670 Back to development for the next patch release. 2013-03-16 12:11:37 +01:00
nitper 15f2776d77 update cluster tests 2013-03-11 23:08:10 +00:00
nitper dc1103582e allow HMSET in clusters 2013-03-11 22:50:03 +00:00
Daniele Alessandri 8c0498a893 Bump VERSION and update CHANGELOG. 2013-02-18 15:03:45 +01:00
Daniele Alessandri aba24d0f6f Remove a few more unnecessary strict comparisons. 2013-02-17 20:46:31 +01:00
Daniele Alessandri a1df4f20da Expose private method used to parse URI strings.
This is useful for 3rd party libraries such as PredisServiceProvider
so it makes sense to have it public and static.
2013-02-17 20:38:46 +01:00
Daniele Alessandri 116eaba75e Deprecate the whole Predis\Helpers class. 2013-02-17 15:21:01 +01:00
Daniele Alessandri de4bae3f9b Prevent E_NOTICE messages on __destruct() if $parameters is not set.
This can only happen when throwing on invalid connection parameters,
this is a quick fix that will do for now.
2013-02-17 15:21:01 +01:00
Daniele Alessandri b50a5975ac Use a nicer looking version for the same code. 2013-02-17 15:21:01 +01:00
Daniele Alessandri 1c1b4096f6 Remove a few more unnecessary strict comparisons. 2013-02-17 15:20:57 +01:00
Daniele Alessandri f8e4ba152f Use hexadecimal notation. 2013-02-17 12:27:45 +01:00
Daniele Alessandri 6b6b73f5f4 Add getArgument($index) in Predis\Command\CommandInterface.
This method should have been part of the interface since start since
it is used through the library. We also do not specify a default value
for the index argument since it does not make much sense.
2013-02-17 12:22:00 +01:00
Daniele Alessandri 729e40d6c0 Use 0 to indicate no arguments for KEYS[] in Lua scripted commands.
We previously used FALSE for that but in the end it does not make much
sense. Luckily for us this does not represent a breaking change since
existing code will keep to work, so we can safely push this change into
the next patch release.
2013-02-16 17:40:49 +01:00
Daniele Alessandri fbcfdc343e Do not implicitly set multibulk replies on composable connections.
This should be done externally on the underlying protocol instance.
2013-02-16 17:05:38 +01:00
Daniele Alessandri 8cbcb09c4c Remove a few unnecessary strict comparisons. 2013-02-16 17:05:12 +01:00
Daniele Alessandri 2781bd780f Fetch connection factory directly from client options. 2013-02-16 14:36:04 +01:00
Daniele Alessandri 9675626aac Add "tcp_nodelay" in the list of supported connection parameters.
[ci skip]
2013-02-16 12:14:05 +01:00
Daniele Alessandri f8829985f7 Tweak CHANGELOG.
[ci skip]
2013-02-16 11:59:43 +01:00
Daniele Alessandri e2e809c9d4 Fix test to handle different hashes generated on 64bits builds of PHP.
Just noticed it on Travis CI as they recently switched to 64bits PHP builds,
also great to see that the library seems to work fine anyway.
2013-02-16 11:46:42 +01:00
Daniele Alessandri ef0067e1a4 Use slightly faster version of the same code.
Yet again, micro-optimizations here.
2013-02-16 11:32:29 +01:00
Daniele Alessandri da343046e5 No need for strict comparison here.
Should be also slightly faster (at least on a micro-optimization scale).
2013-02-16 11:14:55 +01:00
Daniele Alessandri 76d6681f68 Fix unserialization of Predis/Connection/PhpiredisStreamConnection. 2013-02-16 11:14:55 +01:00
Daniele Alessandri 8d01be388d Support TCP_NODELAY for stream-based connections on PHP >= 5.4.0.
This cannot be implemented for previous versions of PHP because we
need socket_import_stream() to extract the underlying socket resource
from the stream in order to be able to set the TCP_NODELAY flag.
2013-02-16 11:14:50 +01:00
Daniele Alessandri c38376dcc4 Accepts callable as first argument of Predis\Client::__construct().
Users can then use callables to wrap the creation and initialization
of the underlying connection with custom strategies:
2013-02-15 11:36:19 +01:00
Daniele Alessandri c354d02105 Aggregated connection for redis-cluster now uses 16384 hash slots.
This commit reflects the recent change from the redis unstable branch
in which the number of hash slots was increased from 4096 to 16384.

See https://github.com/antirez/redis/commit/ebd666d for reference.
2013-02-14 21:54:23 +01:00
Daniele Alessandri d6685a424a Update FAQ. 2013-02-09 10:11:16 +01:00
Daniele Alessandri 05ef62ff97 Tweak README. 2013-02-09 10:06:30 +01:00
Daniele Alessandri b76e876b73 No need for "iterable_multibulk" as a default connection parameter.
Furthermore, connections that do not support certain parameters should
check and throw whether they are set, independently from their value.
2013-02-09 09:56:07 +01:00
Daniele Alessandri ebd895a67d Update link to phpiredis. 2013-02-09 09:49:57 +01:00
Daniele Alessandri 923e7ed5fd Add Predis\Connection\PhpiredisStreamConnection.
This class works just like Predis\Connection\PhpiredisConnection but
it does not require the socket extensions since it relies on PHP's
native streams thus allowing the use of persistent connections.
2013-02-09 09:46:48 +01:00
Daniele Alessandri 9e0fe7bdc7 Back to development for the next patch release.
Forgot to do that prior to merge, my bad.

[ci skip]
2013-02-08 14:54:53 +01:00
Daniele Alessandri 5d774dc581 Merge branch 'gh/pr/102' into v0.8 2013-02-08 12:37:43 +01:00
Daniele Alessandri dffbb8b042 [tests] Fix version check to work on Redis < 2.6.0.
INFO does not have the concept of sections prior to 2.6.0 while our
test suite can be run against 2.4.0 (provided that phpunit.xml uses
the correct server profile), so we need to make sure that the check
does not break with prior version of Redis even if this is unlikely
to happen at this point.

Also changed a bit the naming of variables since things are already
pretty much explicit in this context.
2013-02-08 12:33:07 +01:00
Raphael Stolt 06d5475129 Added PHPUnit utility method markTestSkippedOnRedisVersionBelow and applied it where necessary 2013-02-07 22:46:25 +01:00
Raphael Stolt 1536455fea Added CLIENT SET/GETNAME command 2013-02-07 20:21:29 +01:00
Daniele Alessandri aa1b070f9b Bump VERSION and update CHANGELOG. 2013-02-03 13:59:55 +01:00
Daniele Alessandri 3e125c964c Implement Predis\Session\SessionHandler.
This class makes it easy to use Predis to store PHP sessions on Redis.

NOTE: requires PHP >= 5.4.0 or a polyfill for PHP's SessionHandlerInterface.
2013-02-02 15:14:06 +01:00
Daniele Alessandri e9fdc47d3f Minor fix in readme.
[ci skip]
2013-02-02 10:48:22 +01:00
Daniele Alessandri d3930ea298 Update markdown files to use relative links.
See https://help.github.com/articles/relative-links-in-readmes for details.

[ci skip]
2013-02-02 10:44:54 +01:00
Daniele Alessandri 0efcbb7992 Add a way to get the default value of an option from options.
It is not possible to get the default value of a client option using
either its name or instance:

  $options = array(
    'profile' => function ($options, $option) {
      // instance of Predis\Option\OptionInterface
      $profile = $options->getDefault($option);

      // string representing an option handled by $options,
      // returns NULL if the specified name is not handled.
      $profile = $options->getDefault('profile');

      return $profile;
    },
  );

This addition makes it less awkward to get the default value of an
option, especially when not in the context of a callable option
initializer.
2013-02-02 10:30:53 +01:00
Daniele Alessandri f2af247c63 Push minor changes in the standard executor internals. 2013-01-29 12:38:19 +01:00
Daniele Alessandri a5cf6d72cd Fix standard pipeline executor not parsing raw replies.
This bug was actually introduced right before pushing the stable release
of v0.8.0 in which we moved the responsibility of parsing raw replies
with command parser to consumer classes.

This commit closes #101.
2013-01-29 12:13:01 +01:00
Daniele Alessandri 7173f0c80c Get back to development. 2013-01-29 11:55:47 +01:00
Daniele Alessandri 495e3c6f8a Bump VERSION and update CHANGELOG.
[ci skip]
2013-01-19 11:18:58 +01:00
Daniele Alessandri 3465bc7c1f Add PHP 5.5 to the CI build matrix and switch to branch blacklisting. 2013-01-19 11:16:20 +01:00
Daniele Alessandri 8bf896f81f Update CHANGELOG. 2013-01-19 09:56:51 +01:00
Daniele Alessandri 0085bc08ae Fix CHANGELOG. 2013-01-19 09:46:58 +01:00
Daniele Alessandri f7232b9ddc Highlight PHP code in FAQ. 2013-01-19 09:44:14 +01:00
Daniele Alessandri 95cc528904 Remove <?php tags from README.
It seems like GitHub can now highlight PHP syntax without those ones.
2013-01-19 09:44:09 +01:00
Daniele Alessandri 4a16cb9417 Update .gitignore.
[ci skip]
2013-01-13 13:54:09 +01:00
Daniele Alessandri 94758d5f58 Update CHANGELOG.
[ci skip]
2013-01-13 13:53:22 +01:00
Daniele Alessandri acc0f04bdb Add missing param to phpdoc entry.
[ci skip]
2013-01-13 13:48:50 +01:00
Daniele Alessandri 09de7be7cb Add optional callable to drive extraction of node hash in distributor.
This is mainly in response to the longstanding issue #36 in which my
proposed solution was fine in terms of functionalities, but eventually
never made into the repository since it was far from being clean enough
for my taste.

Now developers can optionally pass a callable object when creating the
hashring instance to decide how the distributor should extract the hash
from a node (really a connection instance) to populate the ring:

  use Predis\Cluster\Distribution\HashRing;
  use Predis\Connection\PredisCluster;

  $servers = array(
    'tcp://10.0.0.1?alias=node01',
    'tcp://10.0.0.2?alias=node02',
  );

  $options = array(
    'nodehash' => function ($connection) {
      return $connection->getParameters()->alias;
    },
    'cluster' => function ($options) {
      $replicas = HashRing::DEFAULT_REPLICAS;
      $hashring = new HashRing($replicas, $options->nodehash);
      $cluster  = new PredisCluster($hashring);

      return $cluster;
    },
  );

  $client = new Predis\Client($servers, $options);

Both HashRing and KetamaPureRing in the Predis\Cluster\Distribution
namespace support this new approach.
2013-01-13 13:10:01 +01:00
Daniele Alessandri 70b279bbdc Bump year in LICENSE.
On a related note, happy new year!

[ci skip]
2013-01-01 13:58:18 +01:00
Daniele Alessandri f8cbbc5b8f Update URL for phpiredis.
Ownership of the repository has been transferred. Thanks again to
@seppo0010, the original author of phpiredis, for his work on the
extension!

[ci skip]
2012-12-28 20:51:34 +01:00
Daniele Alessandri 83720075f3 Make it possible to cast key prefix processor instance to string.
Mostly useful with string interpolation when getting the current prefix
associated to a client instance.
2012-12-27 10:50:25 +01:00
Daniele Alessandri 9901233fa1 Instantiate the current class when subclassing Client.
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.
2012-12-24 14:50:07 +01:00
Daniele Alessandri dcb4093046 Update CHANGELOG. 2012-12-15 17:46:00 +01:00
Daniele Alessandri 1e8af648cb [tests] Add missing tests for Predis\Option\ClientReplication. 2012-12-15 17:22:10 +01:00
Daniele Alessandri ff5e3515c3 Implement callable initializer for "connections" client option. 2012-12-15 17:12:37 +01:00
Daniele Alessandri 59813cd74e Pass option handler instance to callable initializers.
This is just a convention implemented to client options supporting
callable initializers such as "profile", "cluster" and "replication".

This is useful to get a fully-initialized default value and perform
additional operations before returning it. An example with "profile":

  $options = array(
    'commands' => array(
      'test1' => 'Predis\Command\ConnectionEcho',
      'test2' => 'Predis\Command\ConnectionEcho',
    ),
    'profile'  => function ($options, $option) {
      $profile = $option->getDefault($options);

      if (is_array($options->commands)) {
        foreach ($options->commands as $id => $cmd) {
          $profile->defineCommand($id, $cmd);
        }
      }

      return $profile;
    },
  );
2012-12-15 16:47:32 +01:00
Daniele Alessandri bfd96b15dc Fix response parsing with scripted commands after -NOSCRIPT.
This commit fixes issue #94.
2012-12-12 14:28:06 +01:00
Daniele Alessandri 51b466267d Fix missing use directives.
This miss caused a PHP error when Redis did not return a +QUEUED
reply inside a MULTI / EXEC command.
2012-11-21 22:57:23 +01:00
Daniele Alessandri 78f3596566 Merge branch 'tests/update-redis-2.8' into v0.8 2012-11-21 22:13:42 +01:00
Daniele Alessandri ac01c1a17f [tests] TTL and PTTL return -2 on non existing keys in Redis >= 2.8. 2012-11-21 22:09:05 +01:00
Daniele Alessandri 46920c9c77 [tests] Fix expected exception message on wrong key type.
Redis >= 2.8 returns -WRONGTYPE errors instead of -ERR when executing
operations on wrong key type (such as trying to LPUSH on a string key).

Luckily for us, phpunit's @expectedExceptionMessage annotation actually
does not perform an exact match but works on a substring so we just omit
the initial part of the exception message to make the test work.
2012-11-21 21:49:49 +01:00
Daniele Alessandri b26d6103a6 Avoid channels reordering issues when testing UNSUBSCRIBE.
When using UNSUBSCRIBE without a list of channels (which actually means
"unsubscribe from all the subscribed channels") Redis 2.6 does not
guarantee that channels are returned following the same order of
subscription.
2012-11-13 16:44:55 +01:00
Daniele Alessandri a4760d65cd Bump Redis version used in tests on Travis CI.
It seems like Travis CI switched silently to Redis 2.6, which means
that we can enable the full test suite of Predis.
2012-11-13 16:13:07 +01:00
Daniele Alessandri 49aa6f0aca Update performance numbers in the related FAQ section.
We use newer version of PHP, phpiredis, phpredis, Redis and Ubuntu.

[ci skip]
2012-11-08 13:06:24 +01:00
Daniele Alessandri 292f9939b2 Tweak CONTRIBUTING instructions.
[ci skip]
2012-11-03 16:02:43 +01:00
Daniele Alessandri daa36a980d Make Predis\Connection\PhpiredisConnection::write() protected.
We should actually make use of more protected methods in this
class instead of the private ones, this is most likely going
to be addressed in future commits.

See also issue #86 on GitHub for the reason behind this change.
2012-11-03 15:21:52 +01:00
Daniele Alessandri 0b09d2c359 Back to development for the next patch release. 2012-11-03 15:16:01 +01:00
Daniele Alessandri 0a622aee31 Bump VERSION and update CHANGELOG.
Guess what time is it? Yeah, once again... it is release time!
2012-10-23 01:46:05 +02:00
Daniele Alessandri 040dfebd9c Minimum required version of PHP is 5.3.2. 2012-10-23 01:35:22 +02:00
Daniele Alessandri 0e4604a9bb Tweak README. 2012-10-23 01:34:48 +02:00
Daniele Alessandri deb64c295a Add notes about coding standards in CONTRIBUTING.
[ci skip]
2012-10-12 22:43:45 +02:00
Daniele Alessandri aac06a4cd8 Move contributing section from README to CONTRIBUTING.
[ci skip]
2012-10-12 22:32:21 +02:00
Daniele Alessandri dfb5cf2c1b Remove TODO.
We are going to open a new issue on GitHub for a decent
documentation of the library.

[ci skip]
2012-10-12 21:57:50 +02:00
Daniele Alessandri a1024bba53 Merge FAQ files into a single markdown document.
[ci skip]
2012-10-12 21:51:54 +02:00
Daniele Alessandri f357cafb14 Update CHANGELOG.
[ci skip]
2012-10-12 21:39:05 +02:00
Daniele Alessandri 2b4cf80d90 Merge remote-tracking branch 'origin/pr/84' 2012-09-18 18:05:11 +02:00
Ondrej -onovy- Novy 6cd1f03834 Fixed typos. 2012-09-18 17:43:29 +02:00
Daniele Alessandri 732d7f96d1 Extract a strategy class from the replication connection. 2012-09-12 13:09:52 +02:00
Daniele Alessandri b4e1ab106c Update README.
[ci skip]
2012-09-10 14:17:00 +02:00
Daniele Alessandri 5a2c61c02b Update CHANGELOG.
[ci skip]
2012-09-04 18:32:16 +02:00
Daniele Alessandri 1b9e10bdd8 Move command response parsing to the client class.
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.
2012-09-04 14:53:05 +02:00
Daniele Alessandri 37e2b546b0 Fix MULTI / EXEC pipeline executor when using iterable multibulks. 2012-09-04 13:19:26 +02:00
Daniele Alessandri f163e1221b Describe scripted commands (EVAL / EVALSHA) in the README.
[ci skip]
2012-09-04 13:19:18 +02:00
Daniele Alessandri 1bb2ab95ef Use the proper service name.
[ci skip]
2012-09-03 19:52:25 +02:00
Daniele Alessandri ea18afc820 Update the configuration file for Travis CI.
Travis is upgrading their base image and will disable most of
the services at boot (Redis is one of these).
2012-09-03 14:39:11 +02:00
Daniele Alessandri baa91da35b Merge remote-tracking branch 'origin/pr/79' 2012-09-03 14:37:46 +02:00
Pascal Borreli 6d85a81da4 Fixed typo 2012-09-03 11:50:26 +00:00
Daniele Alessandri f913e81b9f Add a reference to Predis\Async in the README.
I think we can indeed start promoting Predis\Async now that it has
reached a fairly stable state (in terms of API and functionalites)
despite still being considered experimental.

[ci skip]
2012-09-03 12:27:45 +02:00
Daniele Alessandri 003b3d180d Add missing test for Predis\Client::getClientFor(). 2012-08-24 12:13:37 +02:00
Daniele Alessandri a6e1ac11d0 Implement separate Predis\Client::getConnectionById().
This method is not part of Predis\ClientInterface because it is being
considered mostly an helper / utility method.
2012-08-24 12:07:56 +02:00
Daniele Alessandri be46ab813c Remove useless default NULL value in method arguments. 2012-08-24 11:55:19 +02:00
Daniele Alessandri 6918de6e4b Restore 0x notation for CRC16 hash generator. 2012-08-24 11:51:00 +02:00
Daniele Alessandri cfa97dd9f5 Fix cs. 2012-08-24 11:49:43 +02:00
Daniele Alessandri 1e8f790ee4 Remove leftover method. 2012-08-24 11:48:42 +02:00
Daniele Alessandri c655affe5f Add Predis\Command\CommandInterface::setRawArguments(). 2012-08-24 11:47:44 +02:00
Daniele Alessandri 457fd6076d CS fixes.
[ci skip]
2012-08-22 11:54:49 +02:00
Daniele Alessandri 282287765b Fix phpdoc comment.
[ci skip]
2012-08-22 11:53:16 +02:00
Daniele Alessandri 00d6160549 Enable code coverage report on Travis CI. 2012-08-22 11:46:31 +02:00
Daniele Alessandri 576163724c Add an example for multibulk reply iterators.
[ci skip]
2012-08-21 18:10:49 +02:00
Daniele Alessandri 9df9dbdedc Promote multibulk response iterators as Predis response objects.
Multibulk response iterators will not be passed anymore to the response
parser method of the command that generated the response. Pipeline and
transaction abstractions still consume interators returned as response
items.
2012-08-21 17:15:14 +02:00
Daniele Alessandri 8d7f5099c4 Add tests for Predis\Connection\RedisCluster (redis-cluster). 2012-08-21 15:24:06 +02:00
Daniele Alessandri e8daf45269 Improve Predis\Pipeline\MultiExecExecutor. 2012-08-19 15:00:45 +02:00
Daniele Alessandri 8567b05459 Specify the server profile in constructor of the connection factory.
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.
2012-08-19 13:57:58 +02:00
Daniele Alessandri 6e76ea6c7e Add tests for classes in the Predis\Protocol\Text namespace.
This was the only namespace containing classes with no tests.
2012-08-02 21:42:11 +02:00
Daniele Alessandri e774a301c4 Add tests for Predis\Connection\ComposableStreamConnection. 2012-08-02 17:25:13 +02:00
Daniele Alessandri 2b85824d74 Apply minor fixes to README.
[ci skip]
2012-08-02 16:15:31 +02:00
Daniele Alessandri 81422b9992 Partially rewrite README.
[ci skip]
2012-08-02 16:10:58 +02:00
Daniele Alessandri c230b6e831 Update TODO.
[ci skip]
2012-08-02 16:10:47 +02:00
Daniele Alessandri 69d4c013f7 Minor change (mostly micro-optimization). 2012-08-02 12:42:53 +02:00
Daniele Alessandri 1ac9cc9c71 Support EVAL and EVALSHA with redis-cluster.
We extract the keys from commands using the second argument of EVAL /
EVALSHA which specifies the number of arguments that must be treated
as keys (used to populate the KEYS table in the Lua script) and then
we check if there is only one key since redis-cluster right now does
not support multi-keys requests.

Our scripted command abstraction is also supported.
2012-08-02 12:08:56 +02:00
Daniele Alessandri b2f59a0c63 Support EVAL and EVALSHA with client-side sharding (Predis cluster).
We extract the keys from commands using the second argument of EVAL /
EVALSHA which specifies the number of arguments that must be treated
as keys (used to populate the KEYS table in the Lua script) and then
we check if all the keys generate the same hash using the usual method.

Our scripted command abstraction is also supported.

It comes without saying that accessing or setting keys from within the
Lua script is something that might not work as expected, so you should
be careful when using EVAL and EVALSHA in the context of client-side
sharding.
2012-08-02 11:38:20 +02:00
Daniele Alessandri 93971bcb45 Merge branch 'command-hash-strategy'
Conflicts:
	lib/Predis/Connection/RedisCluster.php
2012-07-30 20:29:13 +02:00
Daniele Alessandri e54bc31212 Create Predis\Cluster namespace and move related classes / interfaces. 2012-07-30 20:10:14 +02:00
Daniele Alessandri b9eddae96f Fix and optimize node guessing algorithm. 2012-07-30 16:40:53 +02:00
Daniele Alessandri 7683f97ff2 Move the hash generator inside the command hash strategy class.
This should make things a tiny bit faster (we are speaking about micro
optimizations anyway) but more  importantly it results in a better
encapsulation.
2012-07-30 15:42:45 +02:00
Daniele Alessandri f4f9d02551 Improve the slot-guessing algorithm.
This change makes it almost useless to pre-assign slots using connection
parameters, but the functionality will be kept for special cases.
2012-07-30 12:55:41 +02:00
Daniele Alessandri bacdb7ff62 Remove useless "use" directives. 2012-07-29 22:30:31 +02:00
Daniele Alessandri e0c6ad58be Optimize lazy initialization of the slots map.
This is twice faster than before but still kind of slow compared to the
initialization of a cluster with no slots pre-assigned to connections.
2012-07-29 22:07:00 +02:00
Daniele Alessandri febd99421f Pre-associate connections to slot ranges using connection parameters.
This makes it possible to easily assign a connection to a slot range
using the key "slots" in connection parameters like in the following
example:

    $parameters = array(
        'tcp://127.0.0.1:6379?slots=0-1364',
        'tcp://127.0.0.1:6380?slots=1365-2729',
        'tcp://127.0.0.1:6381?slots=2730-4095',
    );

    $options = array('cluster' => 'redis');
    $client = new Predis\Client($parameters, $options);

It is possible to get the full list of slot ranges for each node of
the cluster using redis-cli connected to one of the nodes:

    ./redis-cli -h 127.0.0.1 -p 6379 CLUSTER NODES

The last column in the resulting output contains the slots assigned
to each instance participating to the redis cluster.
2012-07-29 19:38:58 +02:00
Daniele Alessandri 8559c2cb30 Make it possible to pre-associate slots to a connection.
This is useful to avoid guessing the correct server instance at runtime
thus reducing the overhead of a guess-and-try approach since relying on
-ASK or -MOVED responses returned by a node is a costly operation.

For now, you can pre-associate slots using the "cluster" client option:

    $parameters = array(
        'tcp://127.0.0.1:6379',
        'tcp://127.0.0.1:6380',
        'tcp://127.0.0.1:6381',
    );

    $options = array(
        'cluster' => function ($options) {
            $cluster = new Predis\Connection\RedisCluster();

            $cluster->setSlots(0,    1364, '127.0.0.1:6379');
            $cluster->setSlots(1365, 2729, '127.0.0.1:6380');
            $cluster->setSlots(2730, 4095, '127.0.0.1:6381');

            return $cluster;
        },
    );

    $client = new Predis\Client($parameters, $options);

In the future we will make the slots configuration easier by fetching
the configuration from a node using the command "CLUSTER NODES" or by
pre-computing the slices of slots associated to a node when adding it
to the cluster connection object.
2012-07-29 14:14:11 +02:00
Daniele Alessandri 2ba54a0e1c Use specific command hash strategy for redis-cluster.
This is needed because redis-cluster does not support the same commands
or operations that can be performed with our client-side managed predis
cluster.

For example redis-cluster does not support key tagging (that is, parts
of a key enclosed by {...} to hash only that specific part of a key)
and multiple-key operations suchs as MGET, MSET, SDIFF, SUNION or SINTER.
Some multiple-key operations can be performed anyway if the command has
only one key (e.g. "MGET foo" and "MSET foo bar" will not fail).
2012-07-29 13:48:16 +02:00
Daniele Alessandri 024a31d571 Fix wrong binary notation.
Cannot use 0b yet since it is PHP 5.4 only.
2012-07-29 13:33:06 +02:00
Daniele Alessandri d0572b0efc Rename command hash strategy cass used by Predis cluster. 2012-07-29 12:07:48 +02:00
Daniele Alessandri 0f80cb55bb Add suggested extensions to composer.json.
[ci skip]
2012-07-27 16:58:51 +02:00
Daniele Alessandri 55a3fd65a2 More code styling changes. 2012-07-27 12:23:25 +02:00
Daniele Alessandri 6c732c737b Order "use" directives by namespace depth and name.
This is not really useful per se, but at least we use a fixed rule.
2012-07-26 23:04:20 +02:00
Daniele Alessandri 559853bdd5 Move some classes and interfaces in other namespaces. 2012-07-26 22:37:31 +02:00
Daniele Alessandri 28e1e7ad25 Reword some exception messages. 2012-07-26 16:31:09 +02:00
Daniele Alessandri 98112fdb39 Use instance methods to cast values for connection parameters. 2012-07-26 15:49:42 +02:00
Daniele Alessandri 0e8d978348 Minor code styling changes in client class.
[ci skip]
2012-07-26 15:18:09 +02:00
Daniele Alessandri 469f3cc68b Do not accept string or Predis\Profile\ServerProfileInstance in constructor.
To specify a server profile you can just set the "prefix" option in the
client options array passed to the constructor of Predis\Client.
2012-07-26 14:36:01 +02:00
Daniele Alessandri 36f8210864 Rename variable and field. 2012-07-26 12:18:28 +02:00
Daniele Alessandri 81be513eaa Change signature of Predis\Pipeline\PipelineContext constructor.
Now the second argument is a Predis\Pipeline\PipelineExecutorInterface.
2012-07-26 11:33:24 +02:00
Daniele Alessandri 4b73afc316 Add missing tests for Predis\Command\Hash\CommandHashStrategy. 2012-07-25 22:41:14 +02:00
Daniele Alessandri fd6a14d80b Make it possible to set/unset command handlers in CommandHashStrategy. 2012-07-25 22:22:55 +02:00
Daniele Alessandri f2d92f1e29 Expose the command hash strategy from the predis cluster class.
This will be useful to add, remove or change the handler used to
extract, validate and hash the keys of each command instance.
2012-07-25 21:25:50 +02:00
Daniele Alessandri c3cafaa9a6 Remove useless helpers. 2012-07-25 21:18:58 +02:00
Daniele Alessandri 89d6c0e9b6 Extract common PubSub handling logic into a separate abstract class. 2012-07-25 21:07:04 +02:00
Daniele Alessandri 9f51263136 Fix internal flags for MULTI / EXEC and PUB/SUB contexts.
Do not ask me why I was using 0x (the prefix for hexadecimal notation)
when what we really need here is 0b (the prefix for binary notation).
Really, everything was working by sheer coincidence.

Since 0b has been added in PHP 5.4, we rely on plain decimal numbers
to define our constants for now.
2012-07-21 13:48:32 +02:00
Daniele Alessandri a75e52cf5a Update CHANGELOG.
[ci skip]
2012-06-01 12:14:50 +02:00
Irakli Nadareishvili b865cee1f8 Make autoloader act nicer to autoloaders that may be running in parallel. 2012-05-27 21:09:38 +02:00
Daniele Alessandri 1f0be85a3d Add initial support for Redis cluster (Redis v3.0).
This implementation is capable of handling "ASK" and "MOVED" replies
returned by Redis when one of the nodes asks the client for temporary
or permanent redirects of a slot to a different node.

Performances look almost on par with our client-side sharding solution
and everything looks already relatively stable.

Tests are still missing.
2012-05-13 11:13:33 +02:00
Daniele Alessandri 2578e154d4 New commands: BITOP, BITCOUNT (Redis v2.6-dev). 2012-05-17 17:17:35 +02:00
Daniele Alessandri 81da6d2553 TIME can be considered a read-only command in MASTER / SLAVE. 2012-05-17 16:48:08 +02:00
Daniele Alessandri 188eaf035b Fix comment in package.ini.
[ci skip]
2012-05-17 09:53:45 +02:00
Daniele Alessandri 6638776d85 [test] Add tests for pipeline executors. 2012-05-17 09:43:48 +02:00
Daniele Alessandri 511fe0bbab Rewrite the logic used to handle Redis commands with clustering.
The reason for this change is to support the upcoming redis-cluster since
it has different behaviors compared to the client-side cluster implementation
provided by Predis. For example redis-cluster will not support key tags or
certain operations currently available with our client-side implementation.
2012-05-16 16:50:37 +02:00
Daniele Alessandri 56a3e327fc Add a pipeline executor that internally uses MULTI / EXEC.
The actual pipeline is wrapped between MULTI and EXEC to ensure that all the
commands are correctly sent and executed on the server. The whole pipeline is
discarded should it fail at a certain point during execution.

We do not use our Predis\Transaction\MultiExecContext abstraction internally
but rely on raw commands since this new executor does not really need full
support for Redis transactions.
2012-05-15 15:59:23 +02:00
Daniele Alessandri 63cf5048a7 Slightly optimize internals of MULTI / EXEC transactions.
We start using SplQueue to enqueue the commands executed inside a MULTI /
EXEC context so that we can dequeue them for parsing their replies later.

This change does not affect performances but makes the code more clean.
2012-05-15 15:03:42 +02:00
Daniele Alessandri 177d7e1c6a Fix CHANGELOG. 2012-05-15 12:44:49 +02:00
Daniele Alessandri 8a84f8b6cf Optimize command pipelines.
Using SplQueue instead of a plain PHP array to queue command instance in
the pipeline is faster and makes the underlying implementation definitely
more clean.

This change is not going to be backported to Predis v0.7 due to changes
in Predis\Pipeline\PipelineContextInterface.
2012-05-15 12:42:26 +02:00
Daniele Alessandri 09f15a9cb9 Fix README. 2012-05-15 10:38:53 +02:00
Daniele Alessandri 2b4885abc3 Update README.
[ci skip]
2012-04-30 19:56:08 +02:00
Daniele Alessandri 649e71c0ab Clean up leftovers. 2012-04-30 17:07:19 +02:00
Daniele Alessandri 14ac381aad Fix phpdoc. 2012-04-30 17:06:42 +02:00
Daniele Alessandri 78360ea27b Set raw arguments when switching from scripted command to EVAL. 2012-04-28 18:04:54 +02:00
Daniele Alessandri 214b7d2dc9 Remove almost useless method. 2012-04-28 09:23:41 +02:00
Daniele Alessandri 2d0b30dac5 Client handles -NOSCRIPT falling back to EVAL for scripted commands.
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.
2012-04-28 08:56:11 +02:00
Daniele Alessandri 75afe831b0 Fix Predis\Command\ServerEvalSHA::getScriptHash().
"Predis\Command\ServerEvalSHA::getScriptHash()" was calculating the hash
while it just needs to return the first argument of the command.
2012-04-28 01:30:21 +02:00
Daniele Alessandri c36e6fd900 Pass also the command that generated the server error. 2012-04-27 23:59:51 +02:00
Daniele Alessandri 743ccc39ae Remove the "safe" pipeline option.
Just let users specify the appropriate pipeline executor.
2012-04-27 23:36:12 +02:00
Daniele Alessandri 41c29bed4e Change how Redis errors such as -ERR replies generates exceptions.
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.
2012-04-27 23:29:41 +02:00
Daniele Alessandri 90f37f8698 Do not assume that the position of a MultiBulkResponse iterator is 0. 2012-04-27 16:13:15 +02:00
Daniele Alessandri dc889be2a2 Switch to master inside executor when pipelining in replication mode.
The actual code to switch to master has been implemented only in the
standard and the fire-and-forget executor classes since it does not
make sense to have it in special executors used exclusively when in
client-side clustering mode.
2012-04-25 13:02:33 +02:00
Daniele Alessandri ca7bd8149f Scripting abstraction can use negatives to calculate number of keys.
With a negative number Predis will count from the end of the arguments list
to calculate the actual number of keys that will be interpreted as elements
for `KEYS` by the underlying `EVAL` command.
2012-04-25 11:35:06 +02:00
Daniele Alessandri 9426d78b90 Fix example for custom distribution strategy.
See ISSUE #63 for details.
2012-04-13 11:37:03 +02:00
Daniele Alessandri 6b6c91cd15 Update CHANGELOG. 2012-04-01 10:15:31 +02:00
Daniele Alessandri b511911ddb Update README.
[ci skip]
2012-03-31 12:00:10 +02:00
Daniele Alessandri eb450ae0b4 Make 2.6 the default server profile for the test suite too.
We leave 2.4 for Travis CI since their CI environment uses Redis 2.4.

[ci skip]
2012-03-31 11:49:46 +02:00
Daniele Alessandri ca033d15a9 Add a couple of paragraphs in the FAQ. 2012-03-31 11:45:32 +02:00
Daniele Alessandri 9443da1948 Update TODO. 2012-03-31 11:42:25 +02:00
Daniele Alessandri 298dc87613 Update CHANGELOG. 2012-03-31 11:41:43 +02:00
Daniele Alessandri 92697aa075 Make 2.6 the default server profile. 2012-03-31 11:39:42 +02:00
Daniele Alessandri 7c393cbc09 Apparently the next major version or Redis will be 2.8. 2012-03-31 11:36:35 +02:00
Daniele Alessandri 5b6e4dc3dd Make it possible to prepend the autoloader. 2012-03-19 11:31:55 +01:00
Daniele Alessandri b37d072b68 Make Predis\Autoloader faster. 2012-03-18 18:01:01 +01:00
Daniele Alessandri 608c48146f Fix helper method for invalid options in connection classes.
This fix is needed since connection parameters do not respond to __toString()
anymore after recent changes.
2012-03-18 15:23:25 +01:00
Daniele Alessandri bcba9e1009 Change how we check for required extensions in connection classes. 2012-03-18 15:22:24 +01:00
Daniele Alessandri 64a84d9020 Add notes about the parameters supported by each connection class. 2012-03-18 15:07:18 +01:00
Daniele Alessandri a3b4c2244a Remove some overkill stuff from Predis\ConnectionParameters.
It was basically some crazy and almost useless stuff I'd say.
2012-03-18 14:53:21 +01:00
Daniele Alessandri 4805a048ed Add missing tests for Predis\Profile\ServerProfile::defineCommand(). 2012-03-18 13:00:53 +01:00
Daniele Alessandri c394458b80 Remove an almost useless method from Predis\Profile\ServerProfile. 2012-03-18 13:00:28 +01:00
Daniele Alessandri 51d0b58cd3 Add new interfaces used to abstract the main client class and contexts.
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.
2012-03-18 11:05:33 +01:00
Daniele Alessandri 890ace8565 Rename certain connection parameters usig shorter names.
- `connection_async` is now `async_connect`
  - `connection_timeout` is now `timeout`
  - `connection_persistent` is now `persistent`
2012-03-16 19:33:17 +01:00
Daniele Alessandri 8ba432e8f7 Move list of renamed namespaces/interfaces/classes to a separate file. 2012-03-16 19:20:15 +01:00
Daniele Alessandri d61bb3af07 Update CHANGELOG. 2012-03-11 17:50:48 +01:00
Daniele Alessandri da381fffa9 Promote 2.6 as an independent server profile for Redis 2.6.
The development server profile now targets Redis 3.0.
2012-03-11 17:49:18 +01:00
Daniele Alessandri 7b01bc0644 Fix MONITOR and Predis\MonitorContext with Redis 2.6.
Starting with 2.6, Redis uses a slightly different format for the payload
returned by MONITOR for each command. Predis can now adapt to this difference
and returns a new `client` field in the payload object. This new field is
always defined but is set to NULL when connected to Redis < 2.6.

Please note that Redis 2.6 now does not echo the `MONITOR` command as the
first payload when opening a new MONITOR context.
2012-03-11 17:39:57 +01:00
Daniele Alessandri 7893fd1faa New command: TIME (Redis v2.6-dev). 2012-03-11 17:34:06 +01:00
Daniele Alessandri 785670dcf7 Enable integration tests for Redis commands on Travis CI.
This was not possible before since our test suite requires Redis >= 2.4 but
their environment was running Redis 2.2 until a month ago or so.
2012-02-25 10:58:00 +01:00
Daniele Alessandri ec723fd4a9 Remove the ability to execute a command on all the nodes of a cluster.
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.
2012-02-07 15:40:19 +01:00
Daniele Alessandri b67af29eb6 Make cluster and replication classes extend from a common interface. 2012-02-07 11:42:34 +01:00
Daniele Alessandri dc14c29676 Rename certain namespaces, interfaces and classes.
Now we follow a Symfony2-like naming convention for namespaces, interfaces
and classes sticking with one clear rule.

- Renamed namespaces:

  - Predis\Network
  - Predis\Profiles
  - Predis\Iterators
  - Predis\Options
  - Predis\Commands
  - Predis\Commands\Processors

- Renamed interfaces:

  - Predis\IReplyObject
  - Predis\IRedisServerError
  - Predis\IConnectionFactory
  - Predis\IConnectionParameters
  - Predis\Options\IOption
  - Predis\Options\IClientOptions
  - Predis\Profile\IServerProfile
  - Predis\Pipeline\IPipelineExecutor
  - Predis\Distribution\INodeKeyGenerator
  - Predis\Distribution\IDistributionStrategy
  - Predis\Protocol\IProtocolProcessor
  - Predis\Protocol\IResponseReader
  - Predis\Protocol\IResponseHandler
  - Predis\Protocol\ICommandSerializer
  - Predis\Protocol\IComposableProtocolProcessor
  - Predis\Network\IConnection
  - Predis\Network\IConnectionSingle
  - Predis\Network\IConnectionComposable
  - Predis\Network\IConnectionCluster
  - Predis\Network\IConnectionReplication
  - Predis\Commands\ICommand
  - Predis\Commands\IPrefixable
  - Predis\Command\Processor\ICommandProcessor
  - Predis\Command\Processor\ICommandProcessorChain
  - Predis\Command\Processor\IProcessingSupport

- Renamed Classes:

  - Predis\Commands\Command
  - Predis\Network\ConnectionBase

- Classes moved to different namespaces:

  - Predis\MonitorContext

Meh
2012-01-31 18:19:18 +01:00
Daniele Alessandri ce625ca902 Start development of Predis v0.8. 2012-01-15 12:04:17 +01:00
Daniele Alessandri 46d293a716 Fix phpdocs.
[ci skip]
2012-01-14 23:35:39 +01:00
Daniele Alessandri fcd41a4787 Fix CHANGELOG. 2012-01-14 23:33:39 +01:00
Daniele Alessandri 8dc9450f39 Bump year in LICENSE.
[ci skip]
2012-01-14 15:29:12 +01:00
Daniele Alessandri 6ad522f492 Update CHANGELOG. 2012-01-14 15:11:27 +01:00
Daniele Alessandri 317fb6c398 Make Predis\Network\MasterSlaveReplication serializable.
Since PHP cannot serialize closures we switched to a private method to check
if a SORT command is a read-only operation, but closures are still supported
even if they will make the connection unserializable.
2012-01-14 14:53:52 +01:00
Daniele Alessandri 93163bdfb3 Make it possible to serialize and unserialize connection instances. 2012-01-14 14:53:47 +01:00
Daniele Alessandri 626520b836 Add specific server profile alias for Redis 2.6. 2012-01-14 13:01:38 +01:00
Daniele Alessandri aaea628c55 Back to development for the next patch release. 2012-01-14 12:51:01 +01:00
Daniele Alessandri 135fd5004b Bump VERSION and update CHANGELOG. 2011-12-27 16:19:23 +01:00
Daniele Alessandri c25a815317 Update README. 2011-12-27 15:57:12 +01:00
Daniele Alessandri 39a9fc2ddd Add a new example for complex replication configuration. 2011-12-27 12:34:44 +01:00
Daniele Alessandri f5bca54bba Fix CHANGELOG. 2011-12-27 10:40:29 +01:00
Daniele Alessandri 364aa180f9 Use Predis\NotSupportedException for unsupported connection parameters. 2011-12-27 10:38:06 +01:00
Daniele Alessandri 4da506cd9e Track changes in Onion. 2011-12-24 19:03:13 +01:00
Daniele Alessandri ef5034dc72 Update CHANGELOG.
[ci skip]
2011-12-24 12:17:03 +01:00
Daniele Alessandri 994f045a4a Add a method to execute instances of Redis commands in a transaction. 2011-12-24 12:08:34 +01:00
Daniele Alessandri 8cd8d4e580 Fix broken scripted commands when all arguments are considered keys. 2011-12-21 23:14:46 +01:00
Daniele Alessandri 22f1aafae8 Fix example of replication. 2011-12-21 21:41:51 +01:00
Daniele Alessandri c47b009b69 Update documentation for master / slave replication. 2011-12-21 21:33:30 +01:00
Daniele Alessandri 2cff6f0886 Merge branch 'replication' into v0.7
See ISSUE #21 for history and details about transparent master / slave
replication support in Predis.
2011-12-21 21:25:56 +01:00
Daniele Alessandri 63cb150fc2 Switch to the master server right before the execution of a pipeline.
This is actually a temporary solution since we should be using a dedicated
pipeline executor to handle the master/slave replication connection.

Ideally we should stick with a slave if no write operations are present in
the buffer, but forcing a switch to the master connection is the easiest
solution for now and users can still create a new client instance from one
of the slaves when they are sure that their pipelines contain read-only
operations.
2011-12-21 21:15:46 +01:00
Daniele Alessandri 466c666547 Add example for master / slave replication. 2011-12-21 21:01:48 +01:00
Daniele Alessandri 8d5c794efe [tests] Add more tests for Predis\Network\MasterSlaveReplication. 2011-12-21 18:34:53 +01:00
Daniele Alessandri e3fba5d4b7 [tests] Add more tests for Predis\Network\MasterSlaveReplication. 2011-12-21 16:50:03 +01:00
Daniele Alessandri a8400f2ed8 Rename default replication class to Predis\Network\MasterSlaveReplication. 2011-12-21 16:50:03 +01:00
Daniele Alessandri 44629a33db [tests] Add tests for Predis\Network\PredisReplication. 2011-12-21 16:50:01 +01:00
Daniele Alessandri d38f164d3f Minor fixes in documentation. 2011-12-21 11:26:26 +01:00
Daniele Alessandri 9bc5685959 Make the "profile" option accept a callable object as initializer.
This can be useful to set up the profile with additional commands,
e.g. when injecting new "virtual" commands based upon the EVAL and
the Predis\Commands\ScriptedCommand class.
2011-12-21 10:56:10 +01:00
Daniele Alessandri 565690a5b1 Disallow certain commands when in replication mode.
Some of these commands do not actually pose any threat, but their replies can
be misleading since users do not know on which server they are connected to.
We prefer to make things explicit so users can get a new client object out of
a specific connection in the replication pool to issue these commands.
2011-12-20 12:10:06 +01:00
Daniele Alessandri bdbbe18e6c Add the ability to get a connection by alias from aggregated connections.
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.
2011-12-18 15:07:21 +01:00
Daniele Alessandri 78027f0498 Use our own channel to distribute Predis via PEAR instead of PearHub.
The new PEAR channel is self-hosted using Pirum.
2011-12-18 13:28:45 +01:00
Daniele Alessandri 104cd1eae7 Use Onion to build PEAR packages of Predis.
Since Pearhub is currently broken (unfortunately the project does not seem to
be actively mantained anymore) but we still want to have PEAR packages for our
library, we need to automate the creation of a package.xml definition file to
build the actual packages that will be uploaded on a self-hosted PEAR channel.

Onion, while still in its alpha stage right now, works perfectly fine for our
needs and its own package.ini file looks simple enough to edit and maintain.

See http://c9s.github.com/Onion for more details about Onion and thank @c9s
for making it available.
2011-12-18 12:50:10 +01:00
Daniele Alessandri 5148ce16c6 Add the ability to mark specific commands or scripts for EVAL as read-only.
As suggested by @Seldaek in ISSUE #21, it would be desiderable to have the
ability to mark certain scripts for EVAL (and EVALSHA) as read-only to avoid
switching to the master server when this is not really needed.

When marking a script as read-only, only its SHA1 hash is saved to use less
memory and to make this work transparently for both EVAL and EVALSHA.
2011-12-17 10:58:37 +01:00
Daniele Alessandri 4b9ab0bcce Revert to development version for the next patch release.
Forgot to do this early. [ci skip]
2011-12-16 22:04:21 +01:00
Daniele Alessandri 39ed8e3e8f Fix wrong usage of single-quotes for a string expecting interpolation. 2011-12-16 20:36:01 +01:00
Daniele Alessandri 6f4347010d Add new "replication" client option for 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.
2011-12-16 19:30:07 +01:00
Daniele Alessandri c43278eceb Implement support for master / slave replication configurations.
We used a new kind of virtual connection that, just like with cluster, groups
multiple connection objects and handles the logic needed to switch among them
depending on the kind of operation performed by commands..

Our default implementation starts by picking up a random slave and switches to
the master as soon as a command performing a write operation against a key is
detected. The master server will then be used for all the subsequent requests
unless a manual switch to a different connection object is performed.

Redis transactions are always performed on the master server, which means that
the switch to master is done as soon as the client issues a WATCH, MULTI or
any other command related to transactions.

See also https://github.com/nrk/predis/issues/21 for more details.
2011-12-16 16:28:36 +01:00
Daniele Alessandri 5561ce6bd3 Bump VERSION and update CHANGELOG.
Guess what time is it? Yes it is release time!
2011-12-11 17:24:01 +01:00
Daniele Alessandri 6730661b4a [tests] Use a custom phpunit.xml file for TravisCI.
TravisCI environmenti run Redis 2.2 but we need Redis 2.4 to run the
integration tests in the test suite. For now we have simply disabled
the `connected` group of tests using a custom phpunit.xml file, we will
see later if it is worth building Redis on the fly with a `before_script`
directive in .travis.yml or just wait for a a new version of Redis to
be deployed on their VM images.
2011-12-11 17:15:23 +01:00
Daniele Alessandri 4e0c09a3cf Fix E_STRICT error messages. 2011-12-11 16:57:47 +01:00
Daniele Alessandri 71aad42534 Update README and CHANGELOG. 2011-12-11 16:43:42 +01:00
Daniele Alessandri 25b7d72fc9 Make it possible to remove connections from a cluster. 2011-12-11 14:06:32 +01:00
Daniele Alessandri 0f88225104 [tests] Rename the realm-network-webdis group to realm-webdis. 2011-12-11 13:33:37 +01:00
Daniele Alessandri 8be921cb66 Minor changes to README. 2011-12-11 13:20:09 +01:00
Daniele Alessandri e12d80b67f Minor fixes to CHANGELOG. 2011-12-11 13:11:56 +01:00
Daniele Alessandri 8984af1201 Minor changes in comments. 2011-12-11 12:50:27 +01:00
Daniele Alessandri dc9bd4e3a3 Add notice in the example of servers-side scripting with Lua. 2011-12-11 12:41:38 +01:00
Daniele Alessandri 2c4073ecab Prepare CHANGELOG. 2011-12-11 12:36:48 +01:00
Daniele Alessandri 1d6d0f3555 Rename IOption::validate() to IOption::filter(). 2011-12-11 11:07:29 +01:00
Daniele Alessandri efdc16476d Remove explicit versioning from composer.json and add minor changes. 2011-12-11 11:07:29 +01:00
Daniele Alessandri 469d5127aa Update READMEs. 2011-12-11 11:07:23 +01:00
Daniele Alessandri 9dcabc3264 Make it possible to use any type of callable to execute a transaction. 2011-12-11 09:34:41 +01:00
Daniele Alessandri 6e13a8af44 [tests] Add tests for OOB commands inside a pipeline context. 2011-12-11 09:33:00 +01:00
Daniele Alessandri 58e61ae1ca Support for redis-cluster has been postponed to Predis v0.8.
The reason is pretty simple: redis-cluster will not be available in a stable
release of Redis anytime soon, so there is no reason to wait anymore to get
it supported in Predis v0.7.
2011-12-10 15:37:43 +01:00
Daniele Alessandri ab1aeb3c01 Remove BETA from repository. 2011-12-10 15:35:45 +01:00
Daniele Alessandri 1e8446ef63 Update README. 2011-12-10 15:32:15 +01:00
Daniele Alessandri 371ac91777 [tests] Rewrite the whole test suite to allow more granular testing.
Fix also a few bugs found while rewriting the test suite.

In order to be able to run integration tests, the test suite requires
a version of Redis >= 2.4.0.

The units have been splitted into several different groups using
PHPUnit @group annotation to allow developers to enable, disable
and combine certain types of tests. The available groups are:

  - disconnected: can run without a Redis server online
  - connected: active connection to a Redis server is required
  - commands: test dedicated to a specific Redis command
  - slow: performs operations that can slow down execution;

A list of the available groups can be obtained by running

  phpunit --list-groups

Groups of tests can be disabled or enabled via the XML configuration
file or the standard command-line test runner. Please note that due
to a bug in PHPUnit, older versions ignore the --group option when
the group is excluded in the XML configuration file. Please refer to
http://github.com/sebastianbergmann/phpunit/issues/320 for details

Integration tests in the @connected group check if the command being
tested is defined in the selected server profile (see the value of
the TEST_SERVER_VERSION constant in phpunit.xml). If the command is
not defined in the target server profile, the integration test is
automatically marked as skipped.

We also provide an helper script in the bin directory that can be
used to automatically generate a file with the scheleton of a test
case for a Redis command by specifying the name of the class in the
Predis\Commands namespace. For example, to generate a test case for
SET (represented by the Predis\Commands\StringSet class):

  ./bin/generate-command-test.php --class=StringSet

The realm of a command is automatically inferred from the name of the
class, but it can be set using the --realm option.
2011-12-10 15:26:00 +01:00
Daniele Alessandri dd20e8557f Pass the client options to the internal cluster initializer method.
This change is useless right now, but when Predis will support Redis cluster
we will need to have access the connection factory object since it will be
required by our future RedisCluster class.
2011-12-02 15:12:47 +01:00
Daniele Alessandri fae8a79cf2 Fix docs after rename of the executable files in bin/. 2011-12-02 10:15:21 +01:00
Daniele Alessandri da25d6b6ff Fix wrong method visibility. 2011-12-01 23:25:31 +01:00
Daniele Alessandri cad5d16eae Handle key prefixing in a completely different way.
When we will have PHP 5.4 going mainstream and Predis will drop support for
PHP 5.3, we will be able to rely in traits to semplify code and reuse more.
2011-12-01 22:52:11 +01:00
Daniele Alessandri a910807666 Make it possible to use any kind of callable for PUB/SUB callbacks. 2011-12-01 21:21:22 +01:00
Daniele Alessandri d81346641f Make it possible to get the client instance used by a pipeline object. 2011-12-01 20:42:37 +01:00
Daniele Alessandri 1b3478ec2e Rename files in the bin/ directory. 2011-12-01 19:05:12 +01:00
Daniele Alessandri 9ab18ff2b4 Fix fields serialization of the connection parameters class.
This was broken by a recent commit.
2011-12-01 18:52:05 +01:00
Daniele Alessandri 2babb8744a Minor code formatting changes. 2011-12-01 16:53:35 +01:00
Daniele Alessandri d9b95d0bc7 Remove useless backslash in the FQN of server profiles. 2011-12-01 16:33:14 +01:00
Daniele Alessandri d79ff39882 Add missing parameters in the phpdocs of PipelineContext. 2011-12-01 15:36:12 +01:00
Daniele Alessandri 559c5097cd Add the ability to discard the commands queued in a pipeline object. 2011-12-01 15:34:45 +01:00
Daniele Alessandri 367ca6e31e Fix mistype in the ServerEval class. 2011-12-01 14:50:34 +01:00
Daniele Alessandri 4a8c678561 Change how the key prefix processor is initialized.
When the argument of 'profile' is an instance of Predis\Profiles\IServerProfile
the value of the 'prefix' option is ignored, which means that it is up to the
user to initialize the appropriate prefix processor.
2011-12-01 14:42:58 +01:00
Daniele Alessandri a5bf0eb41a Partially rewrite the client options bits and remove some madness.
The concept is now similar to a little dependency-injection system.
2011-12-01 12:26:07 +01:00
Daniele Alessandri 1ae30d62db Returns the SHA1 hash of a Lua script from instances of ServerEval. 2011-12-01 08:49:42 +01:00
Daniele Alessandri 64119bb4a0 Do not use different methods to initialize socket for the PhpiredisConnection. 2011-12-01 08:43:34 +01:00
Daniele Alessandri 1d635c370f Change the visibility of certain methods in the client class. 2011-11-29 17:56:58 +01:00
Daniele Alessandri 1260c91f63 Pass the server profile to a connection initializer callback. 2011-11-29 17:17:08 +01:00
Daniele Alessandri 728afe7fcb Redesign how Predis\ConnectionFactory works and make more sense out of it. 2011-11-29 16:32:37 +01:00
Daniele Alessandri 058e11257b Add phpunit.xml to the list of files ignored by git.
The only configuration file for PHPUnit that we want to track using git is
phpunit.xml.dist since phpunit.xml is intended for user customizations of
the test suite.
2011-11-28 10:26:55 +01:00
Daniele Alessandri 31bd53b1ed Update README. 2011-11-28 10:21:19 +01:00
Daniele Alessandri a894f470bd Add an "autoload.php" utility script for autoloading in examples and tests. 2011-11-28 10:19:11 +01:00
Daniele Alessandri b7c715a03f Rename helper method used to fetch the key tag from a key.
The name "getKeyHashablePart" was too generic for this method after all.
2011-11-28 10:10:28 +01:00
Daniele Alessandri 4882e23319 Move Publish/Subscribe related classes to the Predis\PubSub namespace. 2011-11-28 10:01:21 +01:00
Daniele Alessandri 7bdc2e6195 Rename a few protected/private field names. 2011-11-25 14:17:18 +01:00
Daniele Alessandri 0fee0c6d97 Remove the underscore prefix for names of private/protected fields. 2011-11-25 14:17:11 +01:00
Daniele Alessandri 5d42b41c22 [test] Run the test suite in strict mode and use output colors. 2011-11-18 21:04:07 +01:00
Daniele Alessandri 2c1f349925 [test] Use phpunit to specify the parameters of the server used by the suite.
This is especially handy because users can now easily override them without
modifying the source code of the test suite and just by creating a phpunit.xml
file that is automatically picked up by phpunit over the phpunit.xml.dist file
shipped within the repository.
2011-11-18 20:54:58 +01:00
Daniele Alessandri d784838447 [test] Sleep one second to give BGSAVE the time to complete. 2011-11-18 20:43:48 +01:00
Daniele Alessandri 654907857d Update TODO. 2011-11-18 16:38:19 +01:00
Daniele Alessandri 5d04cf540d New commands: INCRBYFLOAT, HINCRBYFLOAT (Redis v2.6-dev). 2011-11-18 16:10:39 +01:00
Daniele Alessandri 51b998720e New commands: PSETEX, PEXPIRE, PEXPIREAT, PTTL (Redis v2.6-dev). 2011-11-18 15:58:36 +01:00
Daniele Alessandri 4e0709ad3d New command: SCRIPT (Redis v2.6-dev).
See http://redis.io/commands/script.
2011-11-18 13:55:03 +01:00
Daniele Alessandri f75974b5d8 New command: SLOWLOG (Redis 2.2).
See http://redis.io/commands/slowlog.
2011-11-18 12:42:14 +01:00
Daniele Alessandri 5680cd40b5 Support the different replies of CONFIG GET|SET|RESETSTAT. 2011-11-18 12:42:09 +01:00
Daniele Alessandri 43c9900804 Run CI only on the specified branches.
[ci skip]
2011-11-16 21:55:08 +01:00
Daniele Alessandri 5d5001c177 Rename markdown files using the .md extension. 2011-11-13 23:09:09 +01:00
Daniele Alessandri 3d4957b06d Merge pull request #50 from snc/travis-ci
Add support for Travis CI.
2011-11-13 13:54:36 -08:00
H. Westphal 734eba3165 Added travis-ci configuration. 2011-11-13 21:17:32 +01:00
H. Westphal 22cb11d057 Fixed typo. 2011-11-13 21:03:22 +01:00
Daniele Alessandri 20f9c0c155 Update composer.json with the new directive for autoloading. 2011-11-05 14:33:16 +01:00
Daniele Alessandri 29f26b0f9b [phpdoc] Apply a few fixes to the documentation. 2011-10-22 11:41:58 +02:00
Daniele Alessandri 2f23fb03f0 Apply minor changes to the rest of the examples. 2011-10-22 11:07:25 +02:00
Daniele Alessandri d160cd9175 Update the server-side scripting example to use the current Redis unstable. 2011-10-22 10:55:58 +02:00
Daniele Alessandri ce75acf81f Merge pull request #47 from GunioRobot/clean 2011-10-21 08:04:12 -07:00
Gun.io Whitespace Robot b9ca78ac43 Remove whitespace [Gun.io WhitespaceBot] 2011-10-21 10:50:42 -04:00
Daniele Alessandri 4e6ed3f26d [phpdoc] Add API documentation.
This is a starting point to add a documentation of the whole set of APIs and
classes of Predis. The next step will be to actually improve and extend it.
2011-10-21 12:54:24 +02:00
Daniele Alessandri 1f2104ef7f Strip the license header from files when generating the monolithic Predis file. 2011-10-18 17:35:49 +02:00
Daniele Alessandri 9608a97469 Add a license header to PHP files. 2011-10-18 16:41:46 +02:00
Daniele Alessandri b4d5b19b30 Fix a few oversights. 2011-10-18 16:19:12 +02:00
Daniele Alessandri 20d872a306 Apply some coding style changes. 2011-10-18 15:51:55 +02:00
Daniele Alessandri 4251664095 Change the way we enable/disable the safety catch to run the test suite.
Now we check for the presence of the file "test/enable.tests" to enable the
test suite instead of relying on the value of a constant defined in PHP. In
this way we can prevent commiting an enabled test suite by mistake since the
above mentioned file is not tracked by Git (see .gitignore)..
2011-10-15 10:58:02 +02:00
Daniele Alessandri b5affe739c Promote 2.4 as the default Redis server profile. 2011-10-15 10:31:38 +02:00
Daniele Alessandri f19c6335d1 Fix parsing of replies to INFO for Redis 2.4.
The new string format for INFO replies has been postponed to a future version
of Redis, so we just moved the INFO class that handles them to the development
server profile.
2011-10-15 10:21:34 +02:00
Daniele Alessandri e070f83dc5 Test suite: move tests for case-insensitive handling of commands names. 2011-10-02 23:29:03 +02:00
Daniele Alessandri c107d937fa Apply minor code styling change, nothing that really matters. 2011-10-02 23:19:08 +02:00
Daniele Alessandri 68dba4cb12 Ensure that commands are registered in server profiles using lowercase aliases. 2011-10-02 23:16:04 +02:00
Daniele Alessandri f1072f8344 Update composer.json. 2011-10-02 12:18:06 +02:00
Daniele Alessandri 5e41657363 Use the new Predis\Autoloader class for tests and examples. 2011-10-02 12:07:52 +02:00
Daniele Alessandri 04b8a8f75c Update comment to reflect old changes. 2011-10-02 11:55:23 +02:00
Daniele Alessandri f7d9c4bac7 Merge pull request #43 from juriansluiman/feature-uppercase
Now Predis follows the same standard behaviour of PHP (method names are treated
case-insensitively) when invoking methods related to Redis commands, (i.e.
$client->info() or $client->INFO() makes no difference).
2011-10-02 02:40:00 -07:00
Jurian Sluiman 49355b0881 Provide better name for test method
testMagicUppercase()? I don't know where I got that one from...
Just changed the method name to something more describring the
actual test.
2011-09-28 18:05:46 +02:00
Jurian Sluiman c33d053531 Add support for mixed case commands
Commands in Redis are most time written in uppercase, Predis supports
only lowercase commands. This change converts all commands into lower-
case, so MiXeDcAsE and UPPERCASE commands can be created as well.
2011-09-28 17:47:56 +02:00
Jurian Sluiman 0d28395aa0 Remove additional test
CreateCommand and supportsCommand are the only two methods which need
to be changes, so test for only those two methods.
2011-09-28 17:34:21 +02:00
Daniele Alessandri 7ebbe25171 Merge pull request #41 from thumbtack/autoloader
Add an autoloader class for Predis.
2011-09-27 23:18:20 -07:00
Eric Naeseth 1296717c2d Update README.
Signed-off-by: Eric Naeseth <eric@thumbtack.com>
2011-09-27 17:43:51 -07:00
Eric Naeseth 94a2ddeca9 Add an autoloader for Predis classes.
This makes it easier to use Predis in scripts and projects that do
not already include as PSR-0 autoloader.

Signed-off-by: Eric Naeseth <eric@thumbtack.com>
2011-09-27 17:42:06 -07:00
Jurian Sluiman 07584f859c Add methods to test for uppercase and lowercase commands
At this moment predis is only capable of lowercase commands, the test
adds the requirement to provide both lowercase, uppercase (and even
mixed case) commands.
2011-09-26 20:33:45 +02:00
Daniele Alessandri 45a5dd49a7 Merge branch 'multiexec_rewrite' 2011-08-24 16:21:08 +02:00
Daniele Alessandri bd8a44101c Change some exception messages thrown by the transaction class. 2011-08-24 11:59:47 +02:00
Daniele Alessandri 71e7cb1399 Make some methods/fields of the transaction class protected instead of private. 2011-08-24 11:49:31 +02:00
Daniele Alessandri f085d2f97d Use a protected method to get all the flags of a transaction. 2011-08-24 11:46:07 +02:00
Daniele Alessandri 647cd53235 Use 'env' in the shebang instead of looking for php in /usr/bin. 2011-08-23 15:34:25 +02:00
Daniele Alessandri 8fdf935605 Make it possible to exclude certain classes when generating a single file.
The script that is used to create a single file out of the whole repository can
now accept a list of classes (their full FQN is needed) that must be excluded
when generating the file. The list can be passed directly in the command line
(multiple -e parameters) or in text file that contains a class name on each
line (single -E parameter).

The script does also a basic check to ensure that the user is not excluding a
class that is referenced by other subclasses.
2011-08-23 15:13:40 +02:00
Daniele Alessandri 7742fd5a22 Fix the bitmask used to check the state of subscriptions in a PubSub context.
The old code was not really broken, but this one is more appropriate.
2011-08-21 15:24:06 +02:00
Daniele Alessandri 7241fe6419 Make the code that process EXEC replies more easy on the eyes. 2011-08-21 15:18:39 +02:00
Daniele Alessandri d01e738d59 Move the code that executes a transaction block to a separate method. 2011-08-21 15:15:39 +02:00
Daniele Alessandri 0bdcf299c2 Implement a few minor changes. 2011-08-21 14:58:39 +02:00
Daniele Alessandri 03d408ddad Use a single integer to store and check the current state of a transaction. 2011-08-21 14:31:04 +02:00
Daniele Alessandri 3733e790eb Reuse code. 2011-08-21 11:55:08 +02:00
Daniele Alessandri a9e34cabb1 Add a composer.json for Composer.
Composer is a package manager for PHP currently under development. You can find
details about Composer at the following URL http://github.com/composer/composer
2011-08-21 10:12:54 +02:00
Daniele Alessandri b30f495345 Change how the hash of a node is retrieved in the hashring-based distributor.
Now classes that extend Predis\Distribution\HashRing can override how the hash
of a node is retrieved in order to implement different kind of distribution
strategies while reusing the base algorithm.
2011-08-20 18:07:42 +02:00
Daniele Alessandri c6fa5232bc Unset multiple fields with just one unset(). 2011-08-20 18:00:58 +02:00
Daniele Alessandri 2b62b75557 Jot down planned improvements for the single-file builder script. 2011-08-20 16:36:11 +02:00
Daniele Alessandri 6151d6d53e Improve actual "namespace" and "use" directives detection (fix ISSUE #35). 2011-08-20 16:30:33 +02:00
Daniele Alessandri db51f6e0d0 Remove spurious carriage returns and trailing spaces. 2011-08-20 16:27:59 +02:00
Daniele Alessandri 62744fbb02 Merge branch 'webdis_connection' 2011-06-29 12:05:24 +02:00
Daniele Alessandri 05b111d6d2 Update comment. 2011-06-29 12:05:00 +02:00
Daniele Alessandri fc51f23ca7 Return cURL errors on HTTP response codes >= 400.
By default the cURL extension does not trigger any error when fetching a
response with an HTTP code >= 400. Changing this behaviour is also useful to
intercept the 403 error generated when executing Redis commands disallowed
on Webdis by using ACLs.
2011-06-08 23:07:23 +02:00
Daniele Alessandri b85bc00880 Add support for the 'connection_timeout' parameter with WebdisConnection. 2011-06-08 22:40:35 +02:00
Daniele Alessandri 86256f2819 Minor reorganization, nothing worth mentioning. 2011-06-08 21:56:11 +02:00
Daniele Alessandri bc040fae6f Change handling of unsupported methods in Predis\Network\WebdisConnection. 2011-06-08 21:53:54 +02:00
Daniele Alessandri e7d2717ee1 Switch from pecl_http to the cURL extension to make requests against Webdis.
The cURL extension is easily found already packaged for various Linux
distributions, it provides everything needed and it seems to have a lower
overhead compared to pecl_http.
2011-06-08 21:43:23 +02:00
Daniele Alessandri 0de4af07dd Fix wrong serialization for commands that do not have arguments. 2011-06-06 12:49:38 +02:00
Daniele Alessandri ff7812282e Slightly change how HTTP options are initialized and handled. 2011-06-06 12:18:37 +02:00
Daniele Alessandri e5a85dfce8 Add support for HTTP basic access authentication.
With Webdis it is possible to define some access control rules to allow or
disallow certain commands and one of the possible configurations is based on
plain old HTTP authentication.
2011-06-05 22:24:03 +02:00
Daniele Alessandri 4b3ba03a3e Disallow certain commands that are disabled or do not make sense with Webdis. 2011-06-05 21:43:55 +02:00
Daniele Alessandri 776501b31a Use a faster approach to serialize the POST data for HTTP requests to Webdis. 2011-06-05 21:01:15 +02:00
Daniele Alessandri 2c8d256bdc Reimport Predis\Network\WebdisConnection with updates.
I could have rebased the previous branch with master, but since it was only a
single file and its history was basically meaningless I preferred to start
from scratch.
2011-06-05 18:46:16 +02:00
Daniele Alessandri 0195a7488a Fix a bug in Predis\Network\StreamConnection::writeBytes().
When PHP's fwrite() ends up writing only a part of $buffer over the network,
this method attempts to automatically write the rest of $buffer. Due to this
bug, the data being already sent to the server was not discarded from $buffer.
Usually fwrite() writes the whole $buffer in one go, but anyway this bug would
not have passed unnoticed since it results in malformed requests and Redis
would have surely returned a -ERR reply.
2011-06-05 17:05:04 +02:00
Daniele Alessandri a8e063e855 Minor updates to README. 2011-06-05 10:19:02 +02:00
Daniele Alessandri 2cdcd297f0 Rename Predis\Client::pubSubContext() to Predis\Client::pubSub(). 2011-06-04 18:47:59 +02:00
Daniele Alessandri 65c0336d2f Use the shared initializer to initialize Pub/Sub and accept callbacks.
When a callback is specified, the client does not return a new instance
of Predis\PubSubContext but it invokes that callback on each message
received. The context can be aborted simply by returning FALSE from the
callback.
2011-06-04 18:41:38 +02:00
Daniele Alessandri 8c207f71bd Removing leftovers. 2011-06-04 17:12:24 +02:00
Daniele Alessandri 42f4663050 Move Predis\ConnectionException to Predis\Network\ConnectionException. 2011-06-04 16:00:35 +02:00
Daniele Alessandri 3801928e0d Move Predis\ProtocolException to Predis\Protocol\ProtocolException. 2011-06-04 15:53:31 +02:00
Daniele Alessandri d59556cae1 Rename the Predis\Protocols namespace to Predis\Protocol. 2011-06-04 15:45:01 +02:00
Daniele Alessandri 667ce97213 Remove a useless return statement. 2011-06-04 15:27:36 +02:00
Daniele Alessandri 101fffbabe Do not check for the existence of a UNIX domain socket file in userland.
Now Predis throws an instance of Predis\ConnectionException when trying to
connect to an instance of Redis on a non-existent UNIX domain socket file.
2011-06-04 15:19:06 +02:00
Daniele Alessandri b35dcd515b Change the code layout of private/protected fields. 2011-06-02 23:49:46 +02:00
Daniele Alessandri df0943120e Add a fire-and-forget executor for pipelines. 2011-06-02 23:44:49 +02:00
Daniele Alessandri b72b12c5cd Fix wrong exception being thrown on invalid instance types passed to 'executor'. 2011-06-02 23:41:12 +02:00
Daniele Alessandri 3f2814c254 Change the code layout of private/protected fields. 2011-06-02 18:53:53 +02:00
Daniele Alessandri 1bd4a25e36 Remove code comments in the exception classes. Descriptions will be added again later with proper documentation. 2011-06-02 18:43:52 +02:00
Daniele Alessandri 24aaa2ac23 Nothing relevant, just reorganizing some private members. 2011-06-02 17:52:22 +02:00
Daniele Alessandri ba4ebb3799 Make the specialized pipeline and transaction initializer methods of Predis\Client protected. 2011-06-02 17:37:33 +02:00
Daniele Alessandri 087b8b0b2b Pass a transaction instance as the first argument of Predis\Transaction\AbortedMultiExecException. 2011-06-02 17:34:06 +02:00
Daniele Alessandri b3910466c9 Clean up a bit the private transaction initializer method of Predis\Client. 2011-06-02 17:25:04 +02:00
Daniele Alessandri 6ad73c782a Move classes related to MULTI/EXEC abstraction to the Predis\Transaction namespace. 2011-06-02 17:20:18 +02:00
Daniele Alessandri fee3866f54 Make it possible to pipeline instances of Predis\Command\ICommand directly. 2011-06-02 16:53:16 +02:00
Daniele Alessandri dfad0d3d24 Rename private variables. 2011-06-02 16:44:52 +02:00
Daniele Alessandri 1ba1408d39 Move the handling of pipeline options inside the pipeline class. 2011-06-02 16:41:17 +02:00
Daniele Alessandri e048ac2d51 Move the PipelineContext class in the Predis\Pipeline namespace. 2011-06-02 16:03:33 +02:00
Daniele Alessandri fb3088b4d3 Redis 2.4 has now its server profile (but it's not the default one yet). The development profile is now Redis 2.6. 2011-05-31 22:01:15 +02:00
Daniele Alessandri 962037321a Make ZADD and ZREM variadic commands for Redis 2.4. 2011-05-31 21:28:37 +02:00
Daniele Alessandri 42521f2558 Do not use new classes for variadic RPUSH, LPUSH, SADD, SREM and HDEL in the 2.4 profile. 2011-05-31 21:26:04 +02:00
Daniele Alessandri 2012963ed0 Add the Predis\IReplyObject interface (used to mark returned objects that must skip reply parsing). 2011-05-21 11:04:15 +02:00
Daniele Alessandri deeb58644a Fix missing change that should have been part of commit cc2349c. 2011-05-21 10:50:42 +02:00
Daniele Alessandri 9f05f50899 Why not using markdown when the FAQ is actually a markdown file? 2011-05-19 12:46:55 +02:00
Daniele Alessandri fd97cd1bad Update the FAQ with details on the missing support for transparent (de)serialization of values. 2011-05-19 12:44:47 +02:00
Daniele Alessandri cc2349c785 Change how error messages for server-side errors are handled. 2011-05-14 10:34:22 +02:00
Daniele Alessandri 8efe76ad60 Make Predis\Commands\ServerEvalSHA inherited from Predis\Commands\ServerEval. 2011-05-13 21:36:25 +02:00
Daniele Alessandri 2010774b68 New command: EVALSHA (Redis v2.4-dev). 2011-05-13 21:23:02 +02:00
Daniele Alessandri 909b06e805 Add support for transparent key prefixing to EVAL and the Predis\Commands\ScriptedCommand class. 2011-05-13 20:22:06 +02:00
Daniele Alessandri 13ac03e134 Merge branch 'redis_scripting' 2011-05-13 20:06:34 +02:00
Daniele Alessandri 092b822033 Add an example for server-side scripting with Lua using Predis abstractions. 2011-05-13 20:06:06 +02:00
Daniele Alessandri 0e0cd865e7 Change the default value for the number of arguments used to populate KEYS. 2011-05-13 19:55:24 +02:00
Daniele Alessandri 228a2ceb2e Update TODO. 2011-05-13 12:58:42 +02:00
Daniele Alessandri b688beaff7 Update FAQ for performance numbers. 2011-05-13 12:58:16 +02:00
Daniele Alessandri 51e0d61833 Update README. 2011-05-13 08:54:28 +02:00
Daniele Alessandri 6e6a28a9f0 Reduce method calls by one when creating a new connection in Predis\Client. 2011-05-07 11:33:33 +02:00
Daniele Alessandri 486225111d Allow developers to define connections with Predis\ConnectionFactory using callable objects. 2011-05-07 10:54:22 +02:00
Daniele Alessandri cd8b3535ef Simplify Predis\ConnectionFactory. 2011-05-07 10:49:32 +02:00
Daniele Alessandri 984fd06c77 Fix check for unsupported parameters in Predis\Network\PhpiredisConnection. 2011-05-07 09:29:50 +02:00
Daniele Alessandri b087b9814d Remove a useless method from the cluster option class. 2011-05-06 16:32:39 +02:00
Daniele Alessandri 4d1dcdb280 Change type check order for client options. 2011-05-06 15:39:22 +02:00
Daniele Alessandri 893618c985 Initialize client options in a lazy way. 2011-05-06 15:20:40 +02:00
Daniele Alessandri 0255261cd5 Whoops, forgot to add Predis\Options\ClientCluster to the repository. 2011-05-05 18:43:08 +02:00
Daniele Alessandri 5f0799ab65 Predis\Commands\Command::filterArguments() is protected. 2011-05-05 17:30:05 +02:00
Daniele Alessandri e97e13bde5 Add the 'prefix' client option. The client will automatically prepend the specified prefix string to each key. 2011-05-05 17:16:14 +02:00
Daniele Alessandri 46db0409a1 Merge branch 'command_processor' 2011-05-05 16:31:55 +02:00
Daniele Alessandri cb363b52f6 Update examples. 2011-05-05 16:24:46 +02:00
Daniele Alessandri 724543a4e6 Rename Predis\Network\ConnectionCluster to Predis\Network\PredisCluster. 2011-05-05 15:55:24 +02:00
Daniele Alessandri 150795f999 Remove the key_distribution option and allow user-defined callbacks to initialize a cluster connection. 2011-05-05 15:06:00 +02:00
Daniele Alessandri 9b4a915a52 Dynamically check for the presence of the WITHSCORE modifier with the ZRANGE family of commands. 2011-05-04 22:44:37 +02:00
Daniele Alessandri b47a3f9ab8 Implement Predis\Commands\Command::setRawArguments(). 2011-05-04 18:52:26 +02:00
Daniele Alessandri 5a80876c64 Handle key prefixing in a more consistent way, and make it a first-class citizen in Predis. 2011-05-04 18:06:40 +02:00
Daniele Alessandri b5ebf48318 Make command processors accept only instances of Predis\Commands\ICommand. 2011-05-04 13:06:57 +02:00
Daniele Alessandri 09b5d7ac61 Change Predis\Commands\Preprocessors to Predis\Command\Processors. 2011-05-04 11:23:31 +02:00
Daniele Alessandri 812d37b2d4 Fix typo. 2011-05-04 00:13:53 +02:00
Daniele Alessandri 859ee35c68 Expose two methods to retrieve the standard prefixing strategies used for commands accepting single or multiple keys. 2011-05-03 21:59:43 +02:00
Daniele Alessandri ca22208ce8 Define a default value for the number of values used to populate KEYS. 2011-05-03 18:00:30 +02:00
Daniele Alessandri a386263d84 Add an helper method to get the actual keys from the whole argument list. 2011-05-03 17:37:39 +02:00
Daniele Alessandri 2bdfed5071 Add an abstraction for a virtual command that leverages Redis scripting with the new EVAL command. 2011-05-03 15:14:51 +02:00
Daniele Alessandri bd027341be New command: EVAL (Redis v2.4-dev). 2011-05-03 15:07:56 +02:00
Daniele Alessandri 4fc87fa161 Update README. 2011-05-03 14:31:35 +02:00
Daniele Alessandri be1f0914b1 Update README's markdown code to show nice colors on GitHub. 2011-04-26 08:26:22 +02:00
Daniele Alessandri 804a5d95fb Merged pull request #28 from udat/110425-fixes.
A few fixes
2011-04-25 15:02:28 -07:00
Artur Kotyrba 650d6a07e0 Fixed method and class name 2011-04-25 23:39:05 +02:00
Artur Kotyrba d4694da487 Added missing use statements 2011-04-25 23:37:00 +02:00
Artur Kotyrba 7e4b367a1d Removed unused argument for constructor of SafeExecutor and SafeClusterExecutor 2011-04-25 23:36:30 +02:00
Daniele Alessandri 19b703588b Add an example for Predis\DispatcherLoop. 2011-04-25 14:39:29 +02:00
Daniele Alessandri b5a1ba9795 Minor code and formatting changes for the example files. 2011-04-25 14:22:34 +02:00
Daniele Alessandri 21372bef6d Add an example for Predis\MonitorContext. 2011-04-25 14:17:24 +02:00
Daniele Alessandri e507198cc3 Do not populate the 'arguments' property if commands received with MONITOR do not carry an argument list. 2011-04-25 14:16:12 +02:00
Daniele Alessandri 88a9f509b4 Fix database handling in Predis\MonitorContext. 2011-04-25 14:14:01 +02:00
Daniele Alessandri b2575bd667 Reorganize some Redis commands classes. 2011-04-25 12:39:21 +02:00
Daniele Alessandri 86edc50ac4 Rename Predis\ConnectionParameters::setByUser(). 2011-04-25 09:49:38 +02:00
Daniele Alessandri dac0104267 Move the reducer function in Predis\Commands\Command::__toString() to a protected method. 2011-04-25 09:48:39 +02:00
Daniele Alessandri f7a3388865 Adjust formatting of Predis\Profiles\ServerVersionNext. 2011-04-25 09:42:07 +02:00
Daniele Alessandri 7b4da3cd78 Use variadic SUBSCRIBE in the PubSub example. 2011-04-24 14:11:34 +02:00
Daniele Alessandri 6dd3565c67 Reuse code. 2011-04-24 14:10:53 +02:00
Daniele Alessandri 3ca4645231 Implement Predis\DispatcherLoop on top of Predis\PubSubContext. 2011-04-24 13:57:19 +02:00
Daniele Alessandri c07c610a0d Implement Predis\MonitorContext as an abstraction for the MONITOR command. 2011-04-24 13:16:16 +02:00
Daniele Alessandri 880d2d24b9 Add the ability to get a connection out of a cluster using a key. 2011-04-24 11:13:33 +02:00
Daniele Alessandri 4ef8948b5e Move the key-tag extractor method to Predis\Helpers. 2011-04-24 11:04:17 +02:00
Daniele Alessandri b396eb3c48 Remove code from the test for SLAVEOF. 2011-04-24 10:03:23 +02:00
Daniele Alessandri b4a90df812 Reuse code. 2011-04-24 09:59:01 +02:00
Daniele Alessandri dd3faa3d53 New command: CLIENT (Redis v2.4-dev). 2011-04-24 09:32:47 +02:00
Daniele Alessandri 819f3ca6ed Implement variadic SREM in the development profile (Redis v2.4). 2011-04-24 09:06:23 +02:00
Daniele Alessandri 8787930977 Implement variadic HDEL in the development profile (Redis v2.4). 2011-04-19 20:49:30 +02:00
Daniele Alessandri a8705b9535 Implement variadic SADD in the development profile (Redis v2.4). 2011-04-19 20:42:50 +02:00
Daniele Alessandri 66f6610c72 Implement variadic RPUSH and LPUSH in the development profile (Redis v2.4). 2011-04-19 20:35:48 +02:00
Daniele Alessandri 9bc50e321e The stable version of Redis will be 2.4. 2011-04-19 20:04:37 +02:00
Daniele Alessandri 4af1b7744c Implement an option to specify which cluster class to use. 2011-04-12 15:42:37 +02:00
Daniele Alessandri cbc7929f6e Move the logic of the connection factory option to a dedicated class. 2011-04-12 14:45:28 +02:00
Daniele Alessandri 1a175da7ca Predis\ConnectionParameters::__isset() should check if a parameters has been set and its value is not null. 2011-04-12 13:01:22 +02:00
Daniele Alessandri f6eab6ec3a Just a minor code styling change. 2011-04-11 23:43:49 +02:00
Daniele Alessandri 07d9717f8a Fix wrong import with the 'use' directive. 2011-04-11 23:23:55 +02:00
Daniele Alessandri 3f3c235363 Make Predis\Distribution\IDistributionStrategy extend the new Predis\Distribution\INodeKeyGenerator interface. 2011-04-11 23:20:10 +02:00
Daniele Alessandri a348e32144 Improve Predis\Client::getConnection() internals. 2011-04-11 18:53:02 +02:00
Daniele Alessandri 55899e7741 Check for null client options in the filter method. 2011-04-11 18:21:05 +02:00
Daniele Alessandri d1157a506d Remove useless code. 2011-04-11 18:21:01 +02:00
Daniele Alessandri bea8a3239d Remove default null value from Predis\Client::initializeConnection(). 2011-04-11 17:01:26 +02:00
Daniele Alessandri 839ce08487 Mostly aesthetic changes for Predis\PubSubContext. 2011-04-11 16:19:22 +02:00
Daniele Alessandri 944a478272 Remove Predis\Commands\Command::setArguments(). 2011-04-09 09:35:28 +02:00
Daniele Alessandri 83bab78bd1 Rename the newConnection() method to create() in Predis\ConnectionFactory. 2011-04-08 16:55:08 +02:00
Daniele Alessandri b22cb6f76d Rename Predis\IConnectionSchemes to Predis\IConnectionFactory. 2011-04-08 16:40:25 +02:00
Daniele Alessandri 5527fc34e7 Split the FAQ file. 2011-04-08 15:23:23 +02:00
Daniele Alessandri 7979d9ab64 Update TODO. 2011-04-08 11:23:02 +02:00
Daniele Alessandri 0396320951 Merge branch 'command_preprocessors' 2011-04-08 11:22:24 +02:00
Daniele Alessandri 20ce6e523a Add an example to show how to leverage key prefixes. 2011-04-08 11:16:06 +02:00
Daniele Alessandri 44d4c77918 Merge branch 'protocols' 2011-04-07 10:34:46 +02:00
Daniele Alessandri 65fc4f7d55 Rename to Predis\Protocols\IComposableProtocolProcessor. 2011-04-07 10:34:30 +02:00
Daniele Alessandri 6e2bba3ddb Move the definition of the OBJECT commad to the server profile for Redis 2.2. 2011-04-06 18:51:52 +02:00
Daniele Alessandri 2a47dc8275 Update TODO. 2011-04-06 18:32:52 +02:00
Daniele Alessandri 9340b3ddce Partially rewrite the shared abstraction initializer method of Predis\Client. 2011-04-06 18:31:53 +02:00
Daniele Alessandri db95350c37 Reorganize the Predis\Protocols namespace. 2011-04-06 17:52:39 +02:00
Daniele Alessandri 80f1cedf79 Add the OBJECT command to the dev server profile. 2011-04-06 12:49:54 +02:00
Daniele Alessandri 33f6419700 Invoke preprocessors after checking if a command is defined by the current profile. 2011-04-05 21:38:49 +02:00
Daniele Alessandri 771a0ee179 Pass method names to preprocessors by value. 2011-04-05 21:34:05 +02:00
Daniele Alessandri 1d9b53d443 Rename class to Predis\Commands\Preprocessors\KeyPrefixPreprocessor. 2011-04-05 20:46:40 +02:00
Daniele Alessandri 86df741937 Rename Predis\Utils to Predis\Helpers. 2011-04-05 16:37:36 +02:00
Daniele Alessandri 4ea8489110 Differentiate communication exceptions between connection and protocol exceptions. 2011-04-05 15:59:21 +02:00
Daniele Alessandri ae96b0f39f Actually implement preprocessor removal from a preprocessor chain. 2011-04-05 13:07:11 +02:00
Daniele Alessandri 1ee7926673 Check values passed to PreprocessorChain::offsetSet(). 2011-04-05 12:37:11 +02:00
Daniele Alessandri b5d748156e Implement a class to chain multiple command preprocessors. 2011-04-05 12:31:00 +02:00
Daniele Alessandri 5e05febf33 Implement a namespacing preprocessor to prefix Redis keys transparently. 2011-04-05 11:57:37 +02:00
Daniele Alessandri 07b4238ce7 Add an abstraction to optionally preprocess command arguments. 2011-04-05 11:56:36 +02:00
Daniele Alessandri c0d9b53ff7 Fix mistakes in the FAQ. 2011-04-04 17:17:33 +02:00
Daniele Alessandri 9954bd5a12 Share more code between connection classes. 2011-04-04 17:17:14 +02:00
Daniele Alessandri 4b25c41f78 Update FAQ. 2011-04-04 15:57:37 +02:00
Daniele Alessandri ac471eeff3 Rename and simplify Predis\Profiles\ServerProfile::registerProfile(). 2011-04-04 15:31:01 +02:00
Daniele Alessandri da2cd3ee7e Simplify Predis\Profiles\ServerProfile::defineCommand(). 2011-04-04 15:03:14 +02:00
Daniele Alessandri bfa9cc7469 Fix FAQ (3300 is actually 3200, just a typo). 2011-04-04 12:17:20 +02:00
Daniele Alessandri 87a1f43487 More updates for the performance numbers in the FAQ. 2011-04-04 12:14:19 +02:00
Daniele Alessandri 556f7b0ec2 Update the average numbers reported in the FAQ after the recent optimizations. 2011-04-04 12:05:35 +02:00
Daniele Alessandri 7146a32f6d Fix the phpiredis-based connection class. 2011-04-04 11:57:47 +02:00
Daniele Alessandri 595692d50f Minor fixes and adjustments to pipeline executors. 2011-04-04 11:42:52 +02:00
Daniele Alessandri c2a61a3113 Rename Predis\CommandPipeline to Predis\PipelineContext. 2011-04-04 11:31:26 +02:00
Daniele Alessandri 27d56a7ac9 Methods for defining commands at runtime should not be enforced in the Predis\Profiles\IServerProfile interface. 2011-04-04 11:23:38 +02:00
Daniele Alessandri 4c84eb181b Minor changes for Predis\Network\StreamConnection. 2011-04-04 11:20:12 +02:00
Daniele Alessandri 503cc86f7d Move method to the base Predis\Network\ConnectionBase class. 2011-04-04 10:37:53 +02:00
Daniele Alessandri 8300b1d63c Fix parameter validation and use a slightly optimized approach. 2011-04-03 13:42:42 +02:00
Daniele Alessandri 62b4e52eef Update TODO. 2011-04-03 12:20:01 +02:00
Daniele Alessandri 8a2f53059a Make Predis\ConnectionParameters::__construct() faster. 2011-04-03 11:36:28 +02:00
Daniele Alessandri c2097b4242 Add .gitignore file. 2011-04-03 11:10:12 +02:00
Daniele Alessandri 6f51521f81 Rename test file. 2011-04-03 10:55:27 +02:00
Daniele Alessandri 609db5159e Remove Predis\ComposableConnectionParameters (superseeded by Predis\ConnectionParameters). 2011-04-03 10:47:37 +02:00
Daniele Alessandri 5ab210fd9d Allow users to undefine a default parameter. 2011-04-03 10:45:47 +02:00
Daniele Alessandri 6fe57bae3f Implement support for using Predis\Options\IOption with Predis\ConnectionParameters. 2011-04-03 10:38:02 +02:00
Daniele Alessandri 9c0012837a Return the path of the file as the identifier for connections based on UNIX domain sockets. 2011-04-03 10:12:28 +02:00
Daniele Alessandri 741495037e Make Predis\ConnectionParameters extensible. 2011-04-03 09:59:54 +02:00
Daniele Alessandri 9d5dc120dc Implement serialization/deserialization for Predis\ConnectionParameters. 2011-04-03 09:32:07 +02:00
Daniele Alessandri 74f6f9ab85 Small refactoring of the internals of Predis\ConnectionParameters. 2011-04-03 09:26:53 +02:00
Daniele Alessandri 0d6ffaf0f0 Minor fixes to the connection parameters classes. 2011-04-01 00:35:02 +02:00
Daniele Alessandri 9389f217c8 Reimplement Predis\ConnectionParameters and move the old implementation to Predis\ComposableConnectionParameters. 2011-03-31 19:13:27 +02:00
Daniele Alessandri 3f720ad867 Add the new Predis\IConnectionParameters interface. 2011-03-31 15:12:52 +02:00
Daniele Alessandri 17f51420ce Improve speed for options validation. 2011-03-31 14:29:06 +02:00
Daniele Alessandri 3d9ddeef98 Opt for a smaller and slightly more efficient solution. 2011-03-31 00:17:11 +02:00
Daniele Alessandri 85c1a7a852 Fix ISSUE #27. 2011-03-30 23:24:45 +02:00
Daniele Alessandri 094a5bcc90 Fix wrong command class for KEYS in the v2.2 server profile. 2011-03-29 00:14:54 +02:00
Daniele Alessandri f637872421 Keeping the server profiles classes independent leads to faster initialization times. 2011-03-28 23:56:31 +02:00
Daniele Alessandri de1a183315 Do some micro-optimizations to reduce the overhead of creating client instances. 2011-03-28 23:25:37 +02:00
Daniele Alessandri ab45b7a461 Fix wrong value :-) 2011-03-28 18:22:54 +02:00
Daniele Alessandri 2ed31eeb16 Update the average performance reported in the FAQ after some internal improvements. 2011-03-28 12:47:46 +02:00
Daniele Alessandri 3b70d1e020 Change code formatting for the classes in the Predis\Commands namespace. 2011-03-27 19:46:02 +02:00
Daniele Alessandri 2201dd1df2 Merge branch 'command_hashing' 2011-03-27 18:50:55 +02:00
Daniele Alessandri 658782f531 Add a script to generate a Phar archive of Predis from the repository. 2011-03-27 15:01:51 +02:00
Daniele Alessandri c4064e5801 Some commands accepting multiple keys can be hashed if all the keys return the same hash (key tags).
List of the commands affected by this change (based on Redis 2.2):
    MSET, MGET, DEL, BLPOP, BRPOP, RPOPLPUSH, BRPOPLPUSH, SINTER, SINTERSTORE,
    SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, ZINTERSTORE, ZUNIONSTORE.
2011-03-27 12:12:51 +02:00
Daniele Alessandri d6a445f1ec Fix SINTERSTORE and SUNIONSTORE to accept an array for the list of source keys. 2011-03-27 09:38:30 +02:00
Daniele Alessandri 0f751cabcb Upgrade to PHPUnit 3.5. 2011-03-27 09:15:38 +02:00
Daniele Alessandri b007b753f5 Fix SUBSCRIBE and PSUBSCRIBE so that they can accept a list of channels for subscription. 2011-03-26 22:45:52 +01:00
Daniele Alessandri b84c84007f Remove Predis\ICommand::canBeHashed() as a public method required for command classes and reuse it internally to Predis\Command. 2011-03-24 00:43:02 +01:00
Daniele Alessandri 4e0e1908cd Minor speed optimizations. 2011-03-23 23:27:05 +01:00
Daniele Alessandri 930a1096e1 Refactor the key hashing logic for Redis commands. 2011-03-23 22:58:49 +01:00
Daniele Alessandri cdfd0b9f5c Fix more problems of Predis\MultiExecContext on certain cases. 2011-03-20 15:27:59 +01:00
Daniele Alessandri cb1f27a403 Update TODO. 2011-03-20 12:08:34 +01:00
Daniele Alessandri e4c45455fe Fix a couple of corner cases for Predis\MultiExecContext. 2011-03-20 10:47:57 +01:00
Daniele Alessandri ceca866158 Remove Predis\Network\IConnectionSingle::setProtocolOption() for now. 2011-03-20 10:47:30 +01:00
Daniele Alessandri 665e428366 Give up on some core reusing practices in favor of execution speed and memory usage. 2011-03-20 10:07:40 +01:00
Daniele Alessandri 31e068796f Update FAQ. 2011-03-18 23:19:25 +01:00
Daniele Alessandri e84844e33c Reimplement Predis\Options\CustomOption to implement directly the Predis\Options\IOption interface. 2011-03-18 22:25:45 +01:00
Daniele Alessandri 21ac9b06f5 Do not validate default option values, assume that default values are correct. 2011-03-18 22:01:50 +01:00
Daniele Alessandri e5ff0ab992 Remove the 'on_connection_initialized' client option (useless in the vast majority of cases). 2011-03-18 21:47:52 +01:00
Daniele Alessandri 743feb0634 Optimize Predis\ClientOptions. 2011-03-18 21:38:37 +01:00
Daniele Alessandri afd78b49a6 Update README and samples. 2011-03-18 15:45:27 +01:00
Daniele Alessandri 94d0805cf6 Apply minor change. 2011-03-18 15:13:52 +01:00
Daniele Alessandri 15af329aa3 Add a new option to specify different connection schemes handlers on a client-instance level. 2011-03-18 15:04:06 +01:00
Daniele Alessandri a10a8e3c9b Clean up code before fast-forwarding the master branch. 2011-03-12 15:56:22 +01:00
Daniele Alessandri 3181f6c592 Remove useless code. 2011-03-12 15:26:39 +01:00
Daniele Alessandri b11ab53bb1 Implement new various changes. 2011-03-11 19:07:46 +01:00
Daniele Alessandri 82759cd6ec Change how parameters are handled internally. 2011-03-10 16:30:00 +01:00
Daniele Alessandri b5a89a0f50 Add a VERSION constant to Predis\Client. 2011-03-08 11:03:32 +01:00
Daniele Alessandri 2853214ddc Update FAQ. 2011-03-08 10:57:06 +01:00
Daniele Alessandri e5e7933aef Add missing file extension. 2011-03-07 19:01:39 +01:00
Daniele Alessandri a81f76d376 Add a FAQ file (it will also be used in the wiki). 2011-03-07 18:59:54 +01:00
Daniele Alessandri 9b17352d9c Transit from UNSTABLE to BETA. 2011-03-07 11:23:26 +01:00
Daniele Alessandri 14d4492ad6 Slight change, nothing serious here. 2011-03-07 11:12:20 +01:00
Daniele Alessandri 995de40734 Fix Predis\Network\PhpiredisConnection. 2011-03-06 16:20:37 +01:00
Daniele Alessandri 190b687789 Change how network resources are handled internally and also exposed to subclasses. 2011-03-06 14:45:59 +01:00
Daniele Alessandri e94fbc3287 Adjust the tests for SUBSTR/GETRANGE since the return value for empty string slices has been fixed in Redis 2.2.2. 2011-03-06 12:45:23 +01:00
Daniele Alessandri 826db07900 Force the test suite to use the 2.2 server profile for now. 2011-03-06 12:39:18 +01:00
Daniele Alessandri be4d01c071 Remove a useless method declaration. 2011-03-06 12:32:43 +01:00
Daniele Alessandri 74b6d05b48 Move the database info parsing bits for the reply to INFO in a separate function. 2011-03-06 12:24:51 +01:00
Daniele Alessandri 58eb4e2477 Make the reply parser for INFO < 2.4 somewhat backwards compatible when used against Redis >= 2.4. 2011-03-06 12:22:22 +01:00
Daniele Alessandri 3186da49b4 Parse allocation_stats if present in the INFO reply. 2011-03-06 12:14:19 +01:00
Daniele Alessandri 3f8b639e2b Change StreamConnection in order to disable read/write timeouts when the value of 'read_write_timeout' is also equal to 0. 2011-03-06 11:03:53 +01:00
Daniele Alessandri 6ce35c97e9 Update README. 2011-03-06 10:08:35 +01:00
Daniele Alessandri b03e6c4f02 Make CommandPipeline::recordCommand() overridable by subclasses. 2011-03-06 10:05:46 +01:00
Daniele Alessandri 7bef3abf91 Change the code for the initialization of the phpiredis reader resource. 2011-03-06 09:55:22 +01:00
Daniele Alessandri 3b4ef4f33e Update README. 2011-03-05 18:42:52 +01:00
Daniele Alessandri f668560a5a Implement ConnectionParameters::__toString(). 2011-03-05 16:36:34 +01:00
Daniele Alessandri 6c9b246b2b Implement a new command class for INFO to parse the new reply format for this command in Redis > 2.2. 2011-03-05 15:47:54 +01:00
Daniele Alessandri 878b338e87 Fix comment. 2011-03-05 12:27:02 +01:00
Daniele Alessandri 38e76cbbc8 Allow passing an array for the list of source keys to be used in ZUNIONSTORE/ZINTERSTORE. 2011-03-05 11:56:10 +01:00
Daniele Alessandri f8844ef8b8 Rename IServerProfile::registerCommand() to IServerProfile::defineCommand(). 2011-03-05 10:52:38 +01:00
Daniele Alessandri 3681c1e622 Clustered connections should use IConnectionSingle::executeCommand() to execute commands internally. 2011-03-05 10:52:31 +01:00
Daniele Alessandri 99c8acfc8c Clean up leftovers. 2011-03-04 19:38:47 +01:00
Daniele Alessandri 30bc53ee75 Rename method ICommand::getCommandId() to ICommand::getId(). 2011-03-04 19:37:10 +01:00
Daniele Alessandri 0d5fb3319d Move Predis\Command and Predis\ICommand to the Predis\Commands namespace. 2011-03-04 19:21:41 +01:00
Daniele Alessandri c3ce210150 Remove a useless 'use' directive. 2011-03-04 18:56:20 +01:00
Daniele Alessandri 8b749386df Implement Predis\Command::__toString(). 2011-03-04 17:17:49 +01:00
Daniele Alessandri 6abac4590d Rename protocol interfaces. 2011-03-04 17:00:23 +01:00
Daniele Alessandri a627b91be1 Fix some more namespacing issues. 2011-03-04 16:18:27 +01:00
Daniele Alessandri 5077996940 Add the ability to create new connections using uri strings, array of parameters or instances of Predis\ConnectionParameters. 2011-03-04 16:06:38 +01:00
Daniele Alessandri db807e7a91 Do not fail on undefined client options properties. 2011-03-04 15:42:45 +01:00
Daniele Alessandri 3021cbd76b Fix wrong name for private field. 2011-03-04 15:12:02 +01:00
Daniele Alessandri 2c4a9e92a6 Add the ability to define custom handlers for client options and connection parameters. 2011-03-04 15:10:43 +01:00
Daniele Alessandri d077aa67db Remove since it generates overhead for nothing really useful. 2011-03-04 13:05:28 +01:00
Daniele Alessandri 0af002b3b9 Update README. 2011-03-04 12:50:12 +01:00
Daniele Alessandri a3218721c1 Merge branch 'phpiredis_new_api' 2011-03-04 12:04:51 +01:00
Daniele Alessandri 21c959a451 Fix namespacing issues with Predis\ClientException. 2011-03-04 11:58:07 +01:00
Daniele Alessandri ba9c3ed3db Fix README. 2011-03-02 17:58:06 +01:00
Daniele Alessandri eba7f84335 Fix the validation of default values for Predis\Options\Option class. 2011-03-02 17:44:10 +01:00
Daniele Alessandri 1e1a904d1a Update the client features test suite (still needs more test coverage of the whole API). 2011-03-02 17:40:27 +01:00
Daniele Alessandri 33ffca23a0 Check if the socket extension is loaded. 2011-03-02 15:16:49 +01:00
Daniele Alessandri 6fada0c35c Add some notes. 2011-03-02 15:00:52 +01:00
Daniele Alessandri 99129fbf91 Implement a new Predis\Network\PhpiredisConnection class that internally uses phpiredis. 2011-03-02 12:28:42 +01:00
Daniele Alessandri 5431af0c4b Update examples to the new connection API design. 2011-03-02 10:11:58 +01:00
Daniele Alessandri 89c311d0de Complete redesign of the network connection API, it should make much more sense now. 2011-03-02 10:02:29 +01:00
Daniele Alessandri fda4f32f06 Rename server profile classes to comply with how underscores are interpreted in the PSR-0 standard. 2011-02-27 13:37:03 +01:00
Daniele Alessandri 9fcdc73499 Fix a couple of namespacing issues with client exceptions in connection classes (ISSUE #24). 2011-02-26 23:19:13 +01:00
Daniele Alessandri ba4dc10936 Update README. 2011-02-26 23:06:25 +01:00
Daniele Alessandri aec3fcf3e8 Add a script to recreate a single PHP file from the repository. 2011-02-26 22:52:40 +01:00
Daniele Alessandri 1746948d61 Fix README (this is the last time). 2011-02-25 14:54:28 +01:00
Daniele Alessandri d05007b5db Fix README (again). 2011-02-25 14:51:16 +01:00
Daniele Alessandri fa08ff1b18 Fix README. 2011-02-25 14:49:53 +01:00
Daniele Alessandri 9e0f0078ea Rename test file. 2011-02-25 14:47:10 +01:00
Daniele Alessandri 4ad4b9cf77 Remove direct requires of parts of PHPUnit. 2011-02-25 12:45:11 +01:00
Daniele Alessandri d2b0326087 Fix requiring path of bootstrap.php in external processes spawned by tests. 2011-02-25 12:39:05 +01:00
Daniele Alessandri 9f58d8a663 Register an autoloader function for examples. 2011-02-25 12:09:45 +01:00
Daniele Alessandri c6b8336346 Update README and remove references to the PHP 5.2 backport for the current version. 2011-02-25 12:06:54 +01:00
Jordi Boggiano ac1e6e1846 Fix tests for latest changes, added phpunit.xml.dist file and autoloader in test/bootstrap.php 2011-02-21 14:18:04 +01:00
Jordi Boggiano 30f908cd85 Convert monolithic one-file-lib to a proper PSR-0 file tree 2011-02-21 14:17:57 +01:00
Daniele Alessandri 2a78142b28 Remove comment, code will remain as-is for performance reasons. 2011-02-16 11:24:08 +01:00
Daniele Alessandri 978d7087d5 Minor formatting change. 2011-02-16 11:22:09 +01:00
Daniele Alessandri 97439a7e08 Update UNSTABLE. 2011-02-16 11:14:41 +01:00
Daniele Alessandri 5a25cf33be Fix after incomplete rename of private fields. 2011-02-15 22:42:18 +01:00
Daniele Alessandri 237f3e3d7e Rename the 'throw_on_error' client option to 'throw_errors'. 2011-02-15 18:24:38 +01:00
Daniele Alessandri 784b33358d Check for null values when setting a protocol processor instance for a connection. 2011-02-15 18:05:02 +01:00
Daniele Alessandri 2b2ca8e8dc Remove trailing spaces in code. 2011-02-15 12:46:19 +01:00
Daniele Alessandri af03916369 Rename a couple of private fields. 2011-02-15 12:30:43 +01:00
Daniele Alessandri 8e4ef21b41 Update CHANGELOG. 2011-02-12 21:57:42 +01:00
Daniele Alessandri 65c530a13b Add test guard for empty bulk replies. 2011-02-12 21:22:47 +01:00
Daniele Alessandri dcafee61a8 Fix wrong method signature. 2011-02-12 21:07:27 +01:00
Jordi Boggiano c1587ccf54 Fixed bug reading zero-length values from a bulk response 2011-02-12 19:16:16 +01:00
Daniele Alessandri 44839a7a6f Minor fix in a comment. 2011-02-12 17:25:48 +01:00
Daniele Alessandri 9d6ffcc5f2 Update TODO. 2011-02-12 17:24:40 +01:00
Daniele Alessandri 679c7ffdd5 Trade a tiny bit of speed for more code reuse. 2011-02-12 16:58:31 +01:00
Daniele Alessandri 09a7a18ac0 Adapt the client features test suite to the new structure of the library. 2011-02-12 16:17:30 +01:00
Daniele Alessandri 03cff6a813 Fix wrong class lookup. 2011-02-12 16:17:00 +01:00
Daniele Alessandri f6bd5757af Repeat with me: do not blindly copy and paste. 2011-02-12 15:34:47 +01:00
Daniele Alessandri 8a145bc6c9 More reliable network writes in Predis\Protocols\TextProtocol. 2011-02-12 15:18:52 +01:00
Daniele Alessandri fc3ee47140 Definitely remove support for Redis 1.0. 2011-02-12 15:05:56 +01:00
Daniele Alessandri ddb3180700 Switch the default text protocol processor to the new and faster one. 2011-02-12 15:05:24 +01:00
Daniele Alessandri f7bef784af Add inline (p)subscribe via options on Predis\PubSubContext initialization. 2011-02-12 14:24:12 +01:00
Daniele Alessandri c5d353b754 Update CHANGELOG. 2011-02-12 14:22:51 +01:00
Daniele Alessandri fb2bc5c8e9 Bump year in LICENSE file. 2011-01-26 16:26:33 +01:00
Daniele Alessandri 68f77444b6 Use only one check for replies that should not be passed to a reply parser. 2011-01-14 17:08:45 +01:00
Daniele Alessandri b6d93b5b34 Promote the profile for Redis 2.2 as stable and use it as the default one for the client. 2011-01-13 12:30:06 +01:00
Daniele Alessandri 4e6ed1fd5b Do not use is_numeric() when it is not really needed (it is relatively slow). 2011-01-13 10:50:40 +01:00
Daniele Alessandri fc9deb68be Remove usage of constants in the protocol handlers. 2011-01-13 10:32:20 +01:00
Daniele Alessandri b47adffd4f Do not perform useless read operations in the bulk reply handler. 2011-01-13 10:28:39 +01:00
Daniele Alessandri 3d80ff489a Less lines of code please. 2011-01-12 22:49:19 +01:00
Daniele Alessandri 4705b27302 Add the 'on_retry' callback as an option for Predis\MultiExecContext. 2011-01-12 22:46:58 +01:00
Daniele Alessandri 9877e0c380 Fix once and for all the implementation of Predis\Protocols\FastTextProtocol. 2011-01-12 22:27:45 +01:00
Daniele Alessandri 92e2455189 Remove unused variables. 2011-01-12 18:39:27 +01:00
Daniele Alessandri e1d8ee5d9b Remove useless reference passing. 2011-01-12 18:38:15 +01:00
Daniele Alessandri 74145ce45c Fix server errors handling in Predis\Protocols\FastTextProtocol. 2011-01-12 18:31:54 +01:00
Daniele Alessandri 898b038f48 Do not make useless calls to fread. 2011-01-12 18:17:35 +01:00
Daniele Alessandri aacf2bc65d Make reads from the socket stream more reliable in Predis\Protocols\FastTextProtocol. 2011-01-12 18:07:09 +01:00
Daniele Alessandri 11c490e978 Minor adjustments. 2011-01-11 22:16:30 +01:00
Daniele Alessandri db2469341f Remove useless stuff. 2011-01-11 16:36:19 +01:00
Daniele Alessandri c1108c6e92 Add an optional and faster protocol processor. 2011-01-10 12:59:04 +01:00
Daniele Alessandri c9567c8606 Add an optional 'on_connection_initialized' callback to client options. 2011-01-10 12:20:52 +01:00
Daniele Alessandri 014adfe75d Remove the method Predis\Network\TcpConnection::rawCommand(). 2011-01-09 21:24:09 +01:00
Daniele Alessandri 2853ba7cef Add the ability to set a different protocol processor on connection instances. 2011-01-09 15:55:55 +01:00
Daniele Alessandri 539a4bbdcd Remove the Predis\ConnectionManager class.
The functionalities provided by that class have been merged into Predis\Client.
2011-01-09 15:42:36 +01:00
Daniele Alessandri bf3776b47a Improve initialization code of Predis\Client instances. 2011-01-09 15:05:28 +01:00
Daniele Alessandri fe0a940f18 Add the new Predis\Protocols\IRedisProtocolExtended interface. 2011-01-09 14:53:20 +01:00
Daniele Alessandri c5169694ef Remove Predis\Client::setConnection(). 2011-01-09 14:41:32 +01:00
Daniele Alessandri a992d9d0b8 Better checks for arguments passed to Predis\ConnectionParameters. 2011-01-09 14:36:48 +01:00
Daniele Alessandri c94cf5ef32 Better checks for arguments passed to Predis\Network\UnixDomainSocketConnection. 2011-01-09 14:36:10 +01:00
Daniele Alessandri 5d37ea1258 Remove useless override. 2011-01-09 14:24:13 +01:00
Daniele Alessandri 8392f465cc Cache the generated connection ID. 2011-01-09 13:43:45 +01:00
Daniele Alessandri b3e7cfe408 Make multiple explicit calls to Predis\Client::connect() possible. 2011-01-09 12:08:05 +01:00
Daniele Alessandri b013bce140 Add missing method in the Predis\Network\IConnectionCluster interface. 2011-01-09 12:01:06 +01:00
Daniele Alessandri f1488534b6 Reduce lines of code. 2011-01-09 11:58:45 +01:00
Daniele Alessandri d1282261b8 Simplify the internals of Predis\Client when initializing a connection.
Note that the options set for a Predis\Network\IConnectionSingle instance
passed to the client are not automatically overridden anymore.
2011-01-09 11:49:40 +01:00
Daniele Alessandri f1223c1a90 Simplify the signature of Predis\Client::pushInitCommands(). 2011-01-09 10:59:54 +01:00
Daniele Alessandri bcfb568d1a Add new Predis\Client::getOptions() method. 2011-01-09 10:53:52 +01:00
Daniele Alessandri 48a47aa503 Add the Predis\Profiles\IServerProfile interface. 2011-01-09 10:46:54 +01:00
Daniele Alessandri 8eb59c8852 Add missing methods to Predis\Protocols\IRedisProtocol. 2011-01-08 20:09:23 +01:00
Daniele Alessandri cd9995b972 Add missing 'use' directives. 2011-01-08 18:18:02 +01:00
Daniele Alessandri 131e4c9d03 Rewrite Predis\ConnectionParams to leverage the Predis\Options namespace. 2011-01-08 18:17:57 +01:00
Daniele Alessandri cd828ab18a Add the new Predis\Options namespace and optimize Predis\ClientOptions. 2011-01-08 17:37:53 +01:00
Daniele Alessandri 33b71a085c Remove a useless method. 2011-01-08 01:57:47 +01:00
Daniele Alessandri 48762ec876 Fix argument name mismatch. 2011-01-08 01:42:39 +01:00
Daniele Alessandri 3b2681f2c8 Move some classes in new namespaces and refactor some code. 2011-01-08 00:45:46 +01:00
Daniele Alessandri 62b8569dc8 More changes but nothing set in stone yet.
The commands test suite passes all the assertions (woot!).
2011-01-07 20:35:39 +01:00
Daniele Alessandri f4309893bc Start a major rewrite of the internals of the library.
The public API of Predis\Client will be compatible with older releases, but
many of the core internal classes are going to change.
2011-01-07 17:50:54 +01:00
Daniele Alessandri c247d6201a Update README. 2011-01-06 11:48:41 +01:00
Daniele Alessandri df268a1143 Merge branch 'pullreq_18' 2011-01-06 00:27:45 +01:00
Daniele Alessandri d04c6d2670 Remove garbage. 2011-01-06 00:25:46 +01:00
Artur Kotyrba 8a76c8c849 Remove unused private method. 2011-01-05 23:03:53 +01:00
Daniele Alessandri a7ae86689d Test suite: client initialization using an instance of Predis\IConnectionCluster. 2011-01-05 23:03:34 +01:00
Daniele Alessandri 6de7222857 Revert changes to the test suite in 6a7fc49. 2011-01-05 22:55:15 +01:00
Artur Kotyrba fb30e22c30 Added possibility to use custom connection cluster classes in Predis\Client constructor.
Added three methods (used by \Predis\Client) to IConnectionSingle interface.
2011-01-05 22:05:21 +01:00
Daniele Alessandri 2bb763dbf6 Minor code style change. 2011-01-05 12:50:33 +01:00
Daniele Alessandri afff56d2ec Look for a closing curly brace in a string only after the index of the opening one when using key tags. 2011-01-05 12:49:08 +01:00
Daniele Alessandri d717d906e8 Optimize when a pair of curly brackets for key tagging is missing. 2011-01-05 12:44:37 +01:00
Daniele Alessandri 51352bcbb3 Create a new class to handle the old response type of KEYS (Redis v1.2). 2011-01-04 21:01:29 +01:00
Daniele Alessandri 2b809325e0 Use a faster method to detect errors when reading a line from the server. 2011-01-04 11:08:55 +01:00
Daniele Alessandri 306ed28c5d Reuse code a bit. 2011-01-04 10:55:39 +01:00
Daniele Alessandri d94ad9942f Use a local cache for handlers while iterating chunks of a multi-bulk reply. 2011-01-04 10:34:56 +01:00
Daniele Alessandri dcbd943505 Reduce overhead by directly managing reply handlers for multibulk replies. 2011-01-04 10:31:34 +01:00
Daniele Alessandri b74e2c22f7 Rewrite the bulk reply handler (more compact and faster code). 2011-01-02 22:22:07 +01:00
Daniele Alessandri 48728d2672 More strict check of the length argument for a network read. 2011-01-02 22:05:27 +01:00
Daniele Alessandri e134e21b78 Remove a useless check for the payload length. 2011-01-02 21:55:28 +01:00
Daniele Alessandri f0f1647773 Use an explicit index to add values to the multi-bulk reply array. 2011-01-02 21:51:53 +01:00
Daniele Alessandri d270205a8c Nothing fancy, and hardly an optimization. 2011-01-02 21:50:14 +01:00
Daniele Alessandri a1db042d17 Avoid allocating an iterator to traverse the arguments list of a command. 2011-01-02 21:42:54 +01:00
Daniele Alessandri e645ed7d3f Preallocate an empty array for empty command arguments. 2011-01-02 21:35:43 +01:00
Daniele Alessandri 9ae69e6c51 Update CHANGELOG. 2011-01-01 19:01:34 +01:00
Daniele Alessandri 3a46f8a7c5 Update README to reflect public API changes. 2011-01-01 15:05:00 +01:00
Daniele Alessandri b202e6cae7 Make Predis\Client::setProfile() private. 2011-01-01 14:59:38 +01:00
Daniele Alessandri 5ca82fb56b Code style changes only. 2011-01-01 13:06:43 +01:00
Daniele Alessandri 1530c7c0d6 Cannot execute with a transaction block after using fluent interface style with Predis\MultiExecBlock. 2011-01-01 11:49:05 +01:00
Daniele Alessandri 11b2f3aca3 Improve fluent-interface style for transactions with CAS. 2011-01-01 11:35:48 +01:00
Daniele Alessandri 6b82b412fb Cannot use the 'retry' option for transactions without providing a function block. 2011-01-01 11:32:23 +01:00
Daniele Alessandri 858d7372ca Add an example for MULTI/EXEC transactions using CAS. 2010-12-31 19:41:31 +01:00
Daniele Alessandri 99042f6b9c Do not fail with empty transactions when using CAS. 2010-12-31 19:40:59 +01:00
Daniele Alessandri 725d5abe4a Update README. 2010-12-31 19:01:01 +01:00
Daniele Alessandri 46b56437a4 Fix and optimize WATCH with CAS. 2010-12-31 18:55:04 +01:00
Daniele Alessandri 4a497eea79 Add support for optional automatic retries upon aborted transactions. 2010-12-31 18:53:13 +01:00
Daniele Alessandri cf21a43211 Support DISCARD with CAS. 2010-12-31 16:17:31 +01:00
Daniele Alessandri 70487bbfb3 Add support for check-and-set (CAS) for transactions with Predis\MultiExecBlock. 2010-12-31 16:17:24 +01:00
Daniele Alessandri 922beeceea Test suite: tiny fix in a test for LINSERT (again, this time for real). 2010-12-31 16:16:54 +01:00
Daniele Alessandri 02d7400d1f Test suite: tiny fix in a test for LINSERT. 2010-12-21 18:36:43 +01:00
Daniele Alessandri 3a2e977ee0 Test suite: temporary fix for testSetExpire(). 2010-12-18 17:44:06 +01:00
Daniele Alessandri eac195eaea Remove unused class. 2010-12-18 17:43:41 +01:00
Daniele Alessandri daf88645f2 New command: BRPOPLPUSH (Redis v2.2-dev).
Conflicts:

	lib/Predis.php
2010-12-18 17:40:30 +01:00
Daniele Alessandri ecc7499b47 Test suite: move up testStrlen(). 2010-12-18 17:39:17 +01:00
Daniele Alessandri 64cb73fbb9 New command: GETBIT (Redis v2.2-dev).
Conflicts:

	lib/Predis.php
2010-12-18 17:38:49 +01:00
Daniele Alessandri 0b9bca8ce1 New command: SETBIT (Redis v2.2-dev).
Conflicts:

	lib/Predis.php
2010-12-18 17:36:48 +01:00
Daniele Alessandri e1924551b1 New command: SETRANGE (Redis v2.2-dev).
Conflicts:

	lib/Predis.php
2010-12-18 17:33:15 +01:00
Daniele Alessandri 171a28fe26 New command: GETRANGE (Redis v2.2-dev - replaces SUBSTR). 2010-12-18 17:29:32 +01:00
Daniele Alessandri ca5cd2f42a Update CHANGELOG. 2010-11-28 21:16:57 +01:00
Daniele Alessandri 1e4661cfc8 Overridden methods for UNWATCH and DISCARD in Predis\MultiExecBlock must return $this to not break method chaining. 2010-11-28 19:19:20 +01:00
Daniele Alessandri e146fa9e65 Fix a bunch of broken tests. 2010-11-28 17:17:39 +01:00
Daniele Alessandri 21b3fdb3e1 New command: PERSIST (Redis v2.2-dev). 2010-11-28 16:35:25 +01:00
Daniele Alessandri cd9dbb763e Update CHANGELOG. 2010-11-28 14:07:03 +01:00
Daniele Alessandri fffa7acd83 Add support for UNIX domain sockets. 2010-11-28 13:58:09 +01:00
Daniele Alessandri f419d15f82 New command: ZREVRANGEBYSCORE (Redis v2.2-dev). 2010-11-27 12:55:39 +01:00
Daniele Alessandri 7b1713398d Make WATCH accept an array of keys that must be watched. 2010-09-25 17:52:04 +02:00
Daniele Alessandri bbc94863d1 Update CHANGELOG. 2010-09-25 16:25:14 +02:00
Daniele Alessandri 9e58ad7648 Test suite: move the trigger guard for the test suite in the common included file. 2010-09-25 16:15:45 +02:00
Daniele Alessandri 1d45649f86 Test suite: add test for WATCH in Predis\MultiExecBlock. 2010-09-25 16:15:13 +02:00
Daniele Alessandri 29f18254bb Fix improper handling of a few corner cases for Predis\MultiExecBlock and send DISCARD to the server on uncaught exceptions inside a callable block. 2010-09-25 15:06:03 +02:00
Daniele Alessandri 4c8f31f532 Fix bad handling of empty MULTI/EXEC contexts (MULTI should not even be sent to the server). 2010-09-25 14:26:50 +02:00
Daniele Alessandri 96e7a58f9f Test suite: add missing tests for Predis\MultiExecBlock. 2010-09-25 14:23:22 +02:00
Daniele Alessandri c131d2f7b6 Fix an infinite recursion loop when calling Predis\Client::multiExec() (ISSUE #14). 2010-09-24 23:02:08 +02:00
Daniele Alessandri fc8f363fe5 New commands: RPUSHX, LPUSHX (Redis v2.2-dev). 2010-09-21 17:33:24 +02:00
Daniele Alessandri 49a140e3af New command: LINSERT (Redis v2.2-dev). 2010-09-21 17:22:26 +02:00
Daniele Alessandri 73b4760fc4 New command: STRLEN (Redis v2.2-dev). 2010-09-21 16:56:52 +02:00
Daniele Alessandri 8f601b8420 Slight micro-optimization. 2010-09-21 15:55:02 +02:00
Daniele Alessandri 4a6bab995e Update CHANGELOG. 2010-09-18 19:09:54 +02:00
Daniele Alessandri b3ea36f3fa Add a new example. 2010-09-18 18:54:57 +02:00
Daniele Alessandri 8769567c7e Remove Predis\Client::rawCommand() due to interface inconsistencies. 2010-09-18 18:31:05 +02:00
Daniele Alessandri 5124fd1d43 Push initialization commands already created instances of Predis\IConnectionSingle. 2010-09-18 18:26:23 +02:00
Daniele Alessandri 2eec3929fb Get the connection out of a cluster only once inside Predis\Client::getClientFor(). 2010-09-18 17:05:54 +02:00
Daniele Alessandri aa0ea048ae Accept instances of Predis\IConnection or Predis\ConnectionParameters as parameters for the constructor of Predis\Client. 2010-09-18 14:56:53 +02:00
Daniele Alessandri f67082072d Perform (p)unsubscribtion only if (p)subscribed to a channel when the destructor for Predis\PubSubContext is invoked. 2010-09-17 22:23:29 +02:00
Daniele Alessandri ac5f34b0ad Make arguments handling more solid for the SORT command. 2010-09-17 22:10:05 +02:00
Daniele Alessandri 78f11f1a0f Remove useless code duplication. 2010-09-17 21:55:25 +02:00
Daniele Alessandri 9468891faf Fix checks on the value for the STORE argument (ISSUE #13). 2010-09-17 11:36:17 +02:00
Daniele Alessandri 9b849d49c0 Remove some ugly code. And by ugly, I mean really ugly. 2010-08-15 19:15:58 +02:00
Daniele Alessandri ff2129bddf Update CHANGELOG. 2010-08-15 18:38:41 +02:00
Daniele Alessandri b40c2fd214 Remove the static method Predis\Client::create(). 2010-08-15 17:58:45 +02:00
Daniele Alessandri 3487cc219c Remove the method Predis\Client::pipelineSafe(). 2010-08-15 17:52:08 +02:00
Daniele Alessandri 4aca9852fe Update CHANGELOG. 2010-08-15 17:29:08 +02:00
Daniele Alessandri 104bfa3332 Move some common code for connections in the Predis\Connection class. 2010-08-15 16:26:37 +02:00
Daniele Alessandri 68397fe958 Rename Predis\Connection to Predis\ConnectionFactory and disallow the creation of instances for this class. 2010-08-15 15:57:24 +02:00
Daniele Alessandri 8428f9916a More code clean-ups. 2010-08-15 15:08:35 +02:00
Daniele Alessandri 2aec7bfeda Leverage the 'use' statement to make the code a bit shorter and cleaner. 2010-08-15 14:52:47 +02:00
Daniele Alessandri 870a7b9e78 Update CHANGELOG. 2010-08-15 12:08:36 +02:00
Daniele Alessandri b4b06acfe7 Add the Predis\ICommand interface. 2010-08-15 11:43:03 +02:00
Daniele Alessandri fbd5059fb2 Do not use magic methods to serialize the command buffer: rename Predis\Command::__invoke() to Predis\Command::serialize(). 2010-08-15 11:34:24 +02:00
Daniele Alessandri c5f6ca1d90 Test suite: update client tests. 2010-08-15 11:25:56 +02:00
Daniele Alessandri 6ea96e82f9 Make Predis\Command::serializeRequest() a protected method. 2010-08-15 11:20:39 +02:00
Daniele Alessandri e7b8b50dda Merge the serialization of Predis\MultiBulkCommand into Predis\Command since it is the preferred way to send commands to Redis servers >= 1.2. 2010-08-15 11:16:30 +02:00
Daniele Alessandri a1f202527f Do not implicitly extract arguments for multi-bulk commands when the first (and only) element of the arguments array is an array itself. 2010-08-15 11:02:48 +02:00
Daniele Alessandri 01b59a546c Update CHANGELOG. 2010-07-30 19:38:51 +02:00
Daniele Alessandri 565a647a3d Drop support for long method names for commands and move support for Redis v1.0 into a separate file under the new addons subdirectory. 2010-07-30 19:19:23 +02:00
Daniele Alessandri 9c494c6052 Update CHANGELOG. 2010-07-11 17:13:59 +02:00
Daniele Alessandri 46f5408490 Update CHANGELOG. 2010-07-11 17:13:49 +02:00
Daniele Alessandri 1520e2c48b Test suite: add a test for the LIMIT modifier of ZRANGEBYSCORE configured via associative array. 2010-07-11 17:07:56 +02:00
Daniele Alessandri 618708b853 Fix handling of the WEIGHT modifier for ZUNIONSTORE and ZINTERSTORE with more than two weights specified. 2010-07-11 16:40:27 +02:00
Daniele Alessandri ad48369a6b Update CHANGELOG. 2010-07-11 16:40:18 +02:00
Daniele Alessandri 802e77da26 Update CHANGELOG. 2010-07-11 15:32:33 +02:00
Daniele Alessandri 4dc1099103 Update CHANGELOG. 2010-07-07 20:17:44 +02:00
Daniele Alessandri f1df8b163c Add tests for the new options handling for the ZRANGE commands family. 2010-07-07 20:17:32 +02:00
Daniele Alessandri 12298d599b Add support for the LIMIT modifier in ZRANGEBYSCORE. 2010-07-07 20:17:22 +02:00
Daniele Alessandri d2cd1b70b8 ZRANGE, ZREVRANGE and ZRANGEBYSCORE accepts an array instance for optional modifiers. 2010-07-07 20:17:14 +02:00
Daniele Alessandri 7d447f8493 More appropriate type-check to detect clustered connections. 2010-07-02 22:38:16 +02:00
Daniele Alessandri 9f21f5a635 Code formatting. 2010-07-02 22:29:47 +02:00
Daniele Alessandri e3ae04437c First stub of the Predis\Compatibility class. Things will most probably change later. 2010-07-02 22:19:57 +02:00
Daniele Alessandri 2af73f4fa1 Deprecate long method names for Redis commands and move them into new server profiles available in the Predis_Compatibility.php file. 2010-07-02 21:52:56 +02:00
Daniele Alessandri a4df829b19 Deprecate Predis\InlineCommand and Predis\BulkCommand. Those classes are made available through the Predis_Compatibility.php file. 2010-07-02 20:58:15 +02:00
Daniele Alessandri 22807e8163 Bump version number. 2010-07-02 20:55:04 +02:00
Daniele Alessandri 1bb04baf78 Merge branch 'version-0.6'
Conflicts:
	lib/Predis.php
2010-07-02 20:51:18 +02:00
Daniele Alessandri f073bc8f70 Update CHANGELOG. 2010-06-30 16:24:02 +02:00
Daniele Alessandri 50970c003b Accept instances of Predis\ConnectionParameters in Predis\Client::__construct(). 2010-06-30 16:17:19 +02:00
Daniele Alessandri 34cfebdc14 Update CHANGELOG. 2010-06-29 20:47:36 +02:00
Daniele Alessandri 735c92089c Update CHANGELOG. 2010-06-29 20:15:03 +02:00
Daniele Alessandri 3853e62d34 Make sure that Predis\MultiExecBlock and Predis\PubSubContext are not initialized over a cluster of connections. 2010-06-29 20:07:57 +02:00
Daniele Alessandri ed105ab4fc Centralize code used to check if a connection object instance is a cluster of connections. 2010-06-29 20:07:51 +02:00
Daniele Alessandri adb489b4d3 Internal optimizations for the Predis\PubSubContext class. 2010-06-29 14:39:24 +02:00
Daniele Alessandri 6d1c4a2096 Update CHANGELOG. 2010-06-26 12:54:12 +02:00
Daniele Alessandri 5cf10ddbfa Add support for variable arguments in Predis\Client::pipeline(). 2010-06-26 12:49:57 +02:00
Daniele Alessandri 5defb14698 Merge branch 'connections' 2010-06-24 17:23:01 +02:00
Daniele Alessandri 3b74aa8f82 Update CHANGELOG. 2010-06-12 11:04:23 +02:00
Daniele Alessandri ac2b718387 Handle aborted MULTI/EXEC transactions. 2010-06-12 10:52:45 +02:00
Daniele Alessandri 89e46912a2 Add support for variable arguments in Predis\Client::multiExec(). 2010-06-12 10:40:28 +02:00
Daniele Alessandri 200d864df3 Parse WATCH and UNWATCH replies as boolean values. 2010-06-12 10:12:07 +02:00
Daniele Alessandri 35b8588dc4 Check for the capabilities of the passed client instance when initializing a \Predis\PubSubContext instance. 2010-06-12 10:06:13 +02:00
Daniele Alessandri 371a7ad69b Less code duplication please. 2010-06-12 09:59:20 +02:00
Daniele Alessandri 5ae476ee16 Check for the capabilities of the passed client instance when initializing a \Predis\MultiExecBlock instance. 2010-06-12 09:52:32 +02:00
Daniele Alessandri f4f19ace0a Adopt more consistent behaviours for \Predis\MultiExecBlock. 2010-06-12 09:27:59 +02:00
Daniele Alessandri 50092d1421 Start adding support for WATCH/UNWATCH to \Predis\MultiExecBlock. 2010-06-12 08:57:42 +02:00
Daniele Alessandri 5287bb2387 New commands: WATCH and UNWATCH (Redis v2.2-dev). 2010-06-12 08:16:00 +02:00
Daniele Alessandri 66a0458018 Leverage the global connection creator facility to alias the 'redis' scheme to the 'tcp' scheme. 2010-06-10 20:37:34 +02:00
Daniele Alessandri 2543bb30cc Map URI schemes to connection classes. 2010-06-10 19:47:40 +02:00
Daniele Alessandri 1fd715e7a5 Prepare to return the appropriate instance of Predis\IConnectionSingle based on the URI scheme. 2010-06-10 11:47:09 +02:00
Daniele Alessandri e191d0a7ce Add required methods to Predis\IConnectionSingle and Predis\IConnectionCluster interfaces. 2010-06-10 11:26:12 +02:00
Daniele Alessandri a2c9cb38a5 Accept the 'scheme' argument for Predis\ConnectionParameters. The 'redis' scheme is now an alias of 'tcp' for compatibility with older versions of Predis. 2010-06-10 11:19:14 +02:00
Daniele Alessandri 72c41a25cb Rename Predis\Connection to Predis\TcpConnection. 2010-06-10 10:40:09 +02:00
Daniele Alessandri b5a6c4e5c3 The Connection class now implements the new IConnectionSingle interface. Guess what is about to happen... 2010-06-09 20:56:25 +02:00
Daniele Alessandri dd07c12402 Clean up leftovers. 2010-06-09 19:10:10 +02:00
Daniele Alessandri 42aa8333fc Slight optimization for multibulk replies. 2010-06-09 19:06:04 +02:00
Daniele Alessandri 25ac5c3ca4 Make Predis\Command::getArguments() a public method. 2010-06-09 19:04:31 +02:00
498 changed files with 49680 additions and 6028 deletions
+7
View File
@@ -0,0 +1,7 @@
*.tgz
*.phar
phpunit.xml
package.xml
composer.lock
experiments/
vendor/
+13
View File
@@ -0,0 +1,13 @@
language: php
php:
- 5.3
- 5.4
- 5.5
branches:
except:
- v0.5
- v0.6
- php5.2_backport
- documentation
services: redis-server
script: phpunit -c phpunit.xml.travisci
-124
View File
@@ -1,124 +0,0 @@
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
+53
View File
@@ -0,0 +1,53 @@
# Namespaces, interfaces and classes renamed in Predis v0.8 #
____________________________________________
Some namespaces, interfaces and classes in Predis v0.8 have been renamed to follow a common rule inspired
by the naming conventions adopted by the Symfony2 project. This is a list of all the changes:
### Renamed namespaces ###
- `Predis\Network` => `Predis\Connection`
- `Predis\Profiles` => `Predis\Profile`
- `Predis\Iterators` => `Predis\Iterator`
- `Predis\Options` => `Predis\Option`
- `Predis\Commands` => `Predis\Command`
- `Predis\Commands\Processors` => `Predis\Command\Processor`
### Renamed interfaces ###
- `Predis\IReplyObject` => `Predis\ResponseObjectInterface`
- `Predis\IRedisServerError` => `Predis\ResponseErrorInterface`
- `Predis\Options\IOption` => `Predis\Option\OptionInterface`
- `Predis\Options\IClientOptions` => `Predis\Option\ClientOptionsInterface`
- `Predis\Profile\IServerProfile` => `Predis\Profile\ServerProfileInterface`
- `Predis\Pipeline\IPipelineExecutor` => `Predis\Pipeline\PipelineExecutorInterface`
- `Predis\Distribution\INodeKeyGenerator` => `Predis\Distribution\HashGeneratorInterface`
- `Predis\Distribution\IDistributionStrategy` => `Predis\Distribution\DistributionStrategyInterface`
- `Predis\Protocol\IProtocolProcessor` => `Predis\Protocol\ProtocolInterface`
- `Predis\Protocol\IResponseReader` => `Predis\Protocol\ResponseReaderInterface`
- `Predis\Protocol\IResponseHandler` => `Predis\Protocol\ResponseHandlerInterface`
- `Predis\Protocol\ICommandSerializer` => `Predis\Protocol\CommandSerializerInterface`
- `Predis\Protocol\IComposableProtocolProcessor` => `Predis\Protocol\ComposableProtocolInterface`
- `Predis\Network\IConnection` => `Predis\Connection\ConnectionInterface`
- `Predis\Network\IConnectionSingle` => `Predis\Connection\SingleConnectionInterface`
- `Predis\Network\IConnectionComposable` => `Predis\Connection\ComposableConnectionInterface`
- `Predis\Network\IConnectionCluster` => `Predis\Connection\ClusterConnectionInterface`
- `Predis\Network\IConnectionReplication` => `Predis\Connection\ReplicationConnectionInterface`
- `Predis\Commands\ICommand` => `Predis\Command\CommandInterface`
- `Predis\Commands\IPrefixable` => `Predis\Command\PrefixableCommandInterface`
- `Predis\Command\Processor\ICommandProcessor` => `Predis\Command\Processor\CommandProcessorInterface`
- `Predis\Command\Processor\ICommandProcessorChain` => `Predis\Command\Processor\CommandProcessorChainInterface`
- `Predis\Command\Processor\IProcessingSupport` => `Predis\Command\Processor\CommandProcessingInterface`
### Renamed classes ###
- `Predis\Commands\Command` => `Predis\Command\AbstractCommand`
- `Predis\Network\ConnectionBase` => `Predis\Connection\AbstractConnection`
### Classes or interfaces moved to different namespaces ###
- `Predis\MonitorContext` => `Predis\Monitor\MonitorContext`
- `Predis\ConnectionParameters` => `Predis\Connection\ConnectionParameters`
- `Predis\ConnectionParametersInterface` => `Predis\Connection\ConnectionParametersInterface`
- `Predis\ConnectionFactory` => `Predis\Connection\ConnectionFactory`
- `Predis\ConnectionFactoryInterface` => `Predis\Connection\ConnectionFactoryInterface`
+573
View File
@@ -0,0 +1,573 @@
v0.8.4 (2013-07-27)
===============================================================================
- Added `DUMP` and `RESTORE` to the server profile for Redis 2.6.
- Connection exceptions now report basic host details in their messages.
- Allow `Predis\Connection\PhpiredisConnection` to use a random IP when a host
actually has several IPs (ISSUE #116).
- __FIX__: allow `HMSET` when using a cluster of Redis nodes with client-side
sharding or redis-cluster (ISSUE #106).
- __FIX__: set `WITHSCORES` modifer for `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`
and `ZREVRANGEBYSCORE` only when the options array passed to these commands
has `WITHSCORES` set to `true` (ISSUE #107).
- __FIX__: scripted commands falling back from `EVALSHA` to `EVAL` resulted in
PHP errors when using a prefixed client (ISSUE #109).
- __FIX__: `Predis\PubSub\DispatcherLoop` now works properly when using key
prefixing (ISSUE #114).
v0.8.3 (2013-02-18)
===============================================================================
- Added `CLIENT SETNAME` and `CLIENT GETNAME` (ISSUE #102).
- Implemented the `Predis\Connection\PhpiredisStreamConnection` class using the
`phpiredis` extension like `Predis\Connection\PhpiredisStreamConnection`, but
without requiring the `socket` extension since it relies on PHP's streams.
- Added support for the TCP_NODELAY flag via the `tcp_nodelay` parameter for
stream-based connections, namely `Predis\Connection\StreamConnection` and
`Predis\Connection\PhpiredisStreamConnection` (requires PHP >= 5.4.0).
- Updated the aggregated connection class for redis-cluster to work with 16384
hash slots instead of 4096 to reflect the recent change from redis unstable
([see this commit](https://github.com/antirez/redis/commit/ebd666d)).
- The constructor of `Predis\Client` now accepts a callable as first argument
returning `Predis\Connection\ConnectionInterface`. Users can create their
own self-contained strategies to create and set up the underlying connection.
- Users should return `0` from `Predis\Command\ScriptedCommand::getKeysCount()`
instead of `FALSE` to indicate that all of the arguments of a Lua script must
be used to populate `ARGV[]`. This does not represent a breaking change.
- The `Predis\Helpers` class has been deprecated and it will be removed in
future releases.
v0.8.2 (2013-02-03)
===============================================================================
- Added `Predis\Session\SessionHandler` to make it easy to store PHP sessions
on Redis using Predis. Please note that this class needs either PHP >= 5.4.0
or a polyfill for PHP's `SessionHandlerInterface`.
- Added the ability to get the default value of a client option directly from
`Predis\Option\ClientOption` using the `getDefault()` method by passing the
option name or its instance.
- __FIX__: the standard pipeline executor was not using the response parser
methods associated to commands to process raw responses (ISSUE #101).
v0.8.1 (2013-01-19)
===============================================================================
- The `connections` client option can now accept a callable object returning
an instance of `Predis\Connection\ConnectionFactoryInterface`.
- Client options accepting callable objects as factories now pass their actual
instance to the callable as the second argument.
- `Predis\Command\Processor\KeyPrefixProcessor` can now be directly casted to
string to obtain the current prefix, useful with string interpolation.
- Added an optional callable argument to `Predis\Cluster\Distribution\HashRing`
and `Predis\Cluster\Distribution\KetamaPureRing` constructor that can be used
to customize how the distributor should extract the connection hash when
initializing the nodes distribution (ISSUE #36).
- Correctly handle `TTL` and `PTTL` returning -2 on non existing keys starting
with Redis 2.8.
- __FIX__: a missing use directive in `Predis\Transaction\MultiExecContext`
caused PHP errors when Redis did not return `+QUEUED` replies to commands
when inside a MULTI / EXEC context.
- __FIX__: the `parseResponse()` method implemented for a scripted command was
ignored when retrying to execute a Lua script by falling back to `EVAL` after
a `-NOSCRIPT` error (ISSUE #94).
- __FIX__: when subclassing `Predis\Client` the `getClientFor()` method returns
a new instance of the subclass instead of a new instance of `Predis\Client`.
v0.8.0 (2012-10-23)
===============================================================================
- The default server profile for Redis is now `2.6`.
- Certain connection parameters have been renamed:
- `connection_async` is now `async_connect`
- `connection_timeout` is now `timeout`
- `connection_persistent` is now `persistent`
- 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 instead are thrown
by the client class and other abstractions such as pipeline contexts.
- Added smart support for redis-cluster (Redis v3.0) in addition to the usual
cluster implementation that uses client-side sharding.
- Various namespaces and classes have been renamed to follow rules inspired by
the Symfony2 naming conventions.
- The second argument of the constructor of `Predis\Client` does not accept
strings or instances of `Predis\Profile\ServerProfileInterface` anymore.
To specify a server profile you must explicitly set `profile` in the array
of client options.
- `Predis\Command\ScriptedCommand` internally relies on `EVALSHA` instead of
`EVAL` thus avoiding to send Lua scripts bodies on each request. The client
automatically resends the command falling back to `EVAL` when Redis returns a
`-NOSCRIPT` error. Automatic fallback to `EVAL` does not work with pipelines,
inside a `MULTI / EXEC` context or with plain `EVALSHA` commands.
- Complex responses are no more parsed by connection classes as they must be
processed by consumer classes using the handler associated to the issued
command. This means that executing commands directly on connections only
returns simple Redis types, but nothing changes when using `Predis\Client`
or the provided abstractions for pipelines and transactions.
- Iterators for multi-bulk replies now skip the response parsing method of the
command that generated the response and are passed directly to user code.
Pipeline and transaction objects still consume automatically iterators.
- Cluster and replication connections now extend a new common interface,
`Predis\Connection\AggregatedConnectionInterface`.
- `Predis\Connection\MasterSlaveReplication` now uses an external strategy
class to handle the logic for checking readable / writable commands and Lua
scripts.
- Command pipelines have been optimized for both speed and code cleanness, but
at the cost of bringing a breaking change in the signature of the interface
for pipeline executors.
- Added a new pipeline executor that sends commands wrapped in a MULTI / EXEC
context to make the execution atomic: if a pipeline fails at a certain point
then the whole pipeline is discarded.
- The key-hashing mechanism for commands is now handled externally and is no
more a competence of each command class. This change is neeeded to support
both client-side sharding and Redis cluster.
- `Predis\Options\Option` is now abstract, see `Predis\Option\AbstractOption`.
v0.7.3 (2012-06-01)
===============================================================================
- New commands available in the Redis v2.6 profile (dev): `BITOP`, `BITCOUNT`.
- When the number of keys `Predis\Commands\ScriptedCommand` is negative, Predis
will count from the end of the arguments list to calculate the actual number
of keys that will be interpreted as elements for `KEYS` by the underlying
`EVAL` command.
- __FIX__: `examples\CustomDistributionStrategy.php` had a mistyped constructor
call and produced a bad distribution due to an error as pointed in ISSUE #63.
This bug is limited to the above mentioned example and does not affect the
classes implemented in the `Predis\Distribution` namespace.
- __FIX__: `Predis\Commands\ServerEvalSHA::getScriptHash()` was calculating the
hash while it just needs to return the first argument of the command.
- __FIX__: `Predis\Autoloader` has been modified to allow cascading autoloaders
for the `Predis` namespace.
v0.7.2 (2012-04-01)
===============================================================================
- Added `2.6` in the server profiles aliases list for the upcoming Redis 2.6.
`2.4` is still the default server profile. `dev` now targets Redis 2.8.
- Connection instances can be serialized and unserialized using `serialize()`
and `unserialize()`. This is handy in certain scenarios such as client-side
clustering or replication to lower the overhead of initializing a connection
object with many sub-connections since unserializing them can be up to 5x
times faster.
- Reworked the default autoloader to make it faster. It is also possible to
prepend it in PHP's autoload stack.
- __FIX__: fixed parsing of the payload returned by `MONITOR` with Redis 2.6.
v0.7.1 (2011-12-27)
===============================================================================
- The PEAR channel on PearHub has been deprecated in favour of `pear.nrk.io`.
- Miscellaneous minor fixes.
- Added transparent support for master / slave replication configurations where
write operations are performed on the master server and read operations are
routed to one of the slaves. Please refer to ISSUE #21 for a bit of history
and more details about replication support in Predis.
- The `profile` client option now accepts a callable object used to initialize
a new instance of `Predis\Profiles\IServerProfile`.
- Exposed a method for MULTI / EXEC contexts that adds the ability to execute
instances of Redis commands against transaction objects.
v0.7.0 (2011-12-11)
===============================================================================
- Predis now adheres to the PSR-0 standard which means that there is no more a
single file holding all the classes of the library, but multiple files (one
for each class). You can use any PSR-0 compatible autoloader to load Predis
or just leverage the default one shipped with the library by requiring the
`Predis/Autoloader.php` and call `Predis\Autoloader::register()`.
- The default server profile for Redis is now 2.4. The `dev` profile supports
all the features of Redis 2.6 (currently unstable) such as Lua scripting.
- Support for long aliases (method names) for Redis commands has been dropped.
- Redis 1.0 is no more supported. From now on Predis will use only the unified
protocol to serialize commands.
- It is possible to prefix keys transparently on a client-level basis with the
new `prefix` client option.
- An external connection factory is used to initialize new connection instances
and developers can now register their own connection classes using the new
`connections` client option.
- It is possible to connect locally to Redis using UNIX domain sockets. Just
use `unix:///path/to/redis.sock` or a named array just like in the following
example: `array('scheme' => 'unix', 'path' => '/path/to/redis.sock');`.
- If the `phpiredis` extension is loaded by PHP, it is now possible to use an
alternative connection class that leverages it to make Predis faster on many
cases, especially when dealing with big multibulk replies, with the the only
downside that persistent connections are not supported. Please refer to the
documentation to see how to activate this class using the new `connections`
client option.
- Predis is capable to talk with Webdis, albeit with some limitations such as
the lack of pipelining and transactions, just by using the `http` scheme in
in the connection parameters. All is needed is PHP with the `curl` and the
`phpiredis` extensions loaded.
- Way too many changes in the public API to make a list here, we just tried to
make all the Redis commands compatible with previous releases of v0.6 so that
you do not have to worry if you are simply using Predis as a client. Probably
the only breaking changes that should be mentioned here are:
- `throw_on_error` has been renamed to `throw_errors` and it is a connection
parameter instead of a client option, along with `iterable_multibulk`.
- `key_distribution` has been removed from the client options. To customize
the distribution strategy you must provide a callable object to the new
`cluster` client option to configure and then return a new instance of
`Predis\Network\IConnectionCluster`.
- `Predis\Client::create()` has been removed. Just use the constructor to set
up a new instance of `Predis\Client`.
- `Predis\Client::pipelineSafe()` was deprecated in Predis v0.6.1 and now has
finally removed. Use `Predis\Client::pipeline(array('safe' => true))`.
- `Predis\Client::rawCommand()` has been removed due to inconsistencies with
the underlying connection abstractions. You can still get the raw resource
out of a connection with `Predis\Network\IConnectionSingle::getResource()`
so that you can talk directly with Redis.
- The `Predis\MultiBulkCommand` class has been merged into `Predis\Command` and
thus removed. Serialization of commands is now a competence of connections.
- The `Predis\IConnection` interface has been splitted into two new interfaces:
`Predis\Network\IConnectionSingle` and `Predis\Network\IConnectionCluster`.
- The constructor of `Predis\Client` now accepts more type of arguments such as
instances of `Predis\IConnectionParameters` and `Predis\Network\IConnection`.
v0.6.6 (2011-04-01)
===============================================================================
- Switched to Redis 2.2 as the default server profile (there are no changes
that would break compatibility with previous releases). Long command names
are no more supported by default but if you need them you can still require
`Predis_Compatibility.php` to avoid breaking compatibility.
- Added a `VERSION` constant to `Predis\Client`.
- Some performance improvements for multibulk replies (parsing them is about
16% faster than the previous version). A few core classes have been heavily
optimized to reduce overhead when creating new instances.
- Predis now uses by default a new protocol reader, more lightweight and
faster than the default handler-based one. Users can revert to the old
protocol reader with the `reader` client option set to `composable`.
This client option can also accept custom reader classes implementing the
new `Predis\IResponseReader` interface.
- Added support for connecting to Redis using UNIX domain sockets (ISSUE #25).
- The `read_write_timeout` connection parameter can now be set to 0 or false
to disable read and write timeouts on connections. The old behaviour of -1
is still intact.
- `ZUNIONSTORE` and `ZINTERSTORE` can accept an array to specify a list of the
source keys to be used to populate the destination key.
- `MGET`, `SINTER`, `SUNION` and `SDIFF` can accept an array to specify a list
of keys. `SINTERSTORE`, `SUNIONSTORE` and `SDIFFSTORE` can also accept an
array to specify the list of source keys.
- `SUBSCRIBE` and `PSUBSCRIBE` can accept a list of channels for subscription.
- __FIX__: some client-side clean-ups for `MULTI/EXEC` were handled incorrectly
in a couple of corner cases (ISSUE #27).
v0.6.5 (2011-02-12)
===============================================================================
- __FIX__: due to an untested internal change introduced in v0.6.4, a wrong
handling of bulk reads of zero-length values was producing protocol
desynchronization errors (ISSUE #20).
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 the constructor of `Predis\Client` in its second
argument as an array or an instance of `Predis\ClientOptions`. For brevity's
sake and compatibility with older versions, the constructor 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 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. `Predis\Distribution\KetamaPureRing`
is an alternative distribution strategy providing 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 specific connections when connected to a
cluster of Redis servers.
- `weight` [default: not set]: allows to balance keys asymmetrically across
multiple servers. This is useful when you have servers with different
amounts of memory 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 socket
resource performs the actual connection.
- `connection_persistent` [default: `FALSE`]: the underlying socket 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 the
`throw_on_error` client option). 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 but returned in the response array as
instances of `Predis\ResponseError` or `Predis\CommunicationException`.
- `Predis\Pipeline\SafeClusterExecutor`: this executor shares the same
behaviour of `Predis\Pipeline\SafeExecutor` but it is geared towards
clustered connections.
- Support for PUB/SUB is handled by the new `Predis\PubSubContext` class, which
could also be used to build a callback dispatcher for PUB/SUB 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.
- `Predis\CommandPipeline` and `Predis\MultiExecBlock` return their instances
when invokink commands, thus allowing method chaining in pipelines and
multi-exec blocks.
- `Predis\MultiExecBlock` 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 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
+40
View File
@@ -0,0 +1,40 @@
## Filing bug reports ##
Bugs or feature requests can be posted online on the [GitHub issues](http://github.com/nrk/predis/issues)
section of the project.
When reporting bugs, in addition to the obvious description of your issue you __must__ always provide
some essential information about your environment such as:
1. version of Predis (check the `VERSION` file or the `Predis\Client::VERSION` constant).
2. version of Redis (check the `redis_version` field returned by [`INFO`](http://redis.io/commands/info)).
3. version of PHP.
4. name and version of the operating system.
5. when possible, a small snippet of code that reproduces the issue.
__Think about it__: we do not have a crystal ball and cannot predict things and peer into the unknown,
so please provide as much details as possible to help us isolating issues and fix them.
__Never__ use GitHub issues to post generic questions about Predis! When you have questions about
how Predis works or how it can be used, please just hop me an email and I will get back to you as
soon as possible.
## Contributing code ##
If you want to work on Predis, it is highly recommended that you first run the test suite in order to
check that everything is OK, and report strange behaviours or bugs. When modifying Predis please make
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`.
The recommended way to contribute to Predis is to fork the project on GitHub, create new topic branches
on your newly created repository to fix or add features (possibly with tests covering your modifications)
and then open a new pull request with a description of the applied changes. Obviously you can use any
other Git hosting provider of your preference.
When writing code please follow the [basic coding (PSR-1)](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
and [coding style (PSR-2)](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
standards and stick with the conventions used in Predis to name classes and interfaces.
Please also follow some basic [commit guidelines](http://git-scm.com/book/ch5-2.html#Commit-Guidelines)
before opening pull requests.
+160
View File
@@ -0,0 +1,160 @@
# Some frequently asked questions about Predis #
_________________________________________________
### What is the point of Predis? ###
The main point of Predis is about offering a highly customizable client for Redis that can be easily
extended by developers while still being reasonabily fast. With Predis you can swap almost any class
used internally with your own custom implementation: you can build connection classes, or new
distribution strategies for client-side sharding, or class handlers to replace existing commands or
add new ones. All of this can be achieved without messing with the source code of the library and
directly in your own application. Given the fast pace at which Redis is developed and adds new
features, this can be a great asset that allows you to add new and still missing features or commands,
or change the behaviour of the library without the need to break your dependencies in production code
(well, at least to some degree).
### Does Predis support UNIX domain sockets and persistent connections? ###
Yes. Obviously, persistent connections actually work when using PHP configured as a persistent process that
gets recycled between requests (see [PHP-FPM](http://php-fpm.org/)).
### Does Predis support transparent (de)serialization of values? ###
No, and it will not ever do that for you by default. The reason behind this decision is that serialization
is usually something that developers prefer to customize depending on their needs and can not be easily
generalized when using Redis because of the many possible access patterns for the data. This does not
mean that it is impossible to have such a feature, you can leverage Predis' extensibility to define your
own serialization-aware commands. See [here](http://github.com/nrk/predis/issues/29#issuecomment-1202624)
for more details on how to implement such a feature with a practical example.
### How can I force Predis to connect to Redis before sending any command? ###
Explicitly connecting to Redis is usually not needed since the client library relies on lazily initialized
connections to the server, but this behavior can be inconvenient in certain scenarios when you absolutely
need to do an upfront check to detect if the server is up and running and eventually catch exceptions on
failures. In this case developers can use `Predis\Client::connect()` to explicitly connect to the server:
```php
$client = new Predis\Client();
try {
$client->connect();
} catch (Predis\Connection\ConnectionException $exception) {
// We could not connect to Redis! Your handling code goes here.
}
$client->info();
```
### How Predis implements abstraction of Redis commands? ###
The approach used in Predis to implement the abstraction of Redis commands is quite simple. By default
every command in the library follows exactly the same argument list as defined in the great online
[Redis documentation](http://redis.io/commands) which makes things pretty easy if you already know how
Redis works or if you need to look up how to use certain commands. Alternatively, variadic commands can
accept an array for keys or values (depending on the command) instead of a list of arguments. See for
example how [RPUSH](http://redis.io/commands/rpush) or [HMSET](http://redis.io/commands/hmset) work:
```php
$client->rpush('my:list', 'value1', 'value2', 'value3'); // values as arguments
$client->rpush('my:list', array('value1', 'value2', 'value3')); // values as single argument array
$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // values as arguments
$client->hmset('my:hash', array('field1'=>'value1', 'field2'=>'value2'); // values as single named array
```
The only exception to this _rule_ is the [SORT](http://redis.io/commands/sort) command for which modifiers are
[passed using a named array](tests/Predis/Command/KeySortTest.php#L56-77).
# Frequently asked questions about performances #
_________________________________________________
### Predis is a pure-PHP implementation: it can not be fast enough! ###
It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you
a couple of easy numbers using a single Predis client with PHP 5.4.7 (custom build) and Redis 2.2
(localhost) under Ubuntu 12.04.1 (running on a Intel Q6600):
21500 SET/sec using 12 bytes for both key and value
21000 GET/sec while retrieving the very same values
0.130 seconds to fetch 30000 keys using _KEYS *_.
How does it compare with a nice C-based extension such as [__phpredis__](http://github.com/nicolasff/phpredis)?
30100 SET/sec using 12 bytes for both key and value
29400 GET/sec while retrieving the very same values
0.035 seconds to fetch 30000 keys using "KEYS *"".
Wow, __phpredis__ looks so much faster! Well we are comparing a C extension with a pure-PHP library so
lower numbers are quite expected, but there is a fundamental flaw in them: is this really how you are
going to use Redis in your application? Are you really going to send thousands of commands in a for-loop
for each page request using a single client instance? If so, well I guess you are probably doing something
wrong. Also, if you need to SET or GET multiple keys you should definitely use commands such as MSET and
MGET. You can also use pipelining to get more performances when this technique can be used.
There is one more thing. We have tested the overhead of Predis by connecting on a localhost instance of
Redis, but how these numbers change when we hit the network by connecting to instances of Redis that
reside on other servers?
Using Predis:
3200 SET/sec using 12 bytes for both key and value
3200 GET/sec while retrieving the very same values
0.132 seconds to fetch 30000 keys using "KEYS *".
Using phpredis:
3500 SET/sec using 12 bytes for both key and value
3500 GET/sec while retrieving the very same values
0.045 seconds to fetch 30000 keys using "KEYS *".
There you go, you get almost the same average numbers and the reason is quite simple: network latency
is a real performance killer and you cannot do (almost) anything about that. As a disclaimer, please
remember that we are measuring the overhead of client libraries implementations and the effects of the
network round-trip time, we are not really measuring how fast Redis is. Redis shines the best with
thousands of concurrent clients doing requests! Also, actual performances should be measured according
to how your application will use Redis.
### I am convinced, but performances for multi-bulk replies (e.g. _KEYS *_) are still worse ###
Fair enough, but there is actually an option for you if you need even more speed and it consists on
installing __[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name)
and let Predis using it. __phpiredis__ is a C-based extension that wraps __hiredis__ (the official
Redis C client library) with a thin layer that exposes its features to PHP. You can choose between
two different connection backend classes: `Predis\Connection\PhpiredisConnection` (it depends on the
`socket` extension) and `Predis\Connection\PhpiredisStreamConnection` (it uses PHP's native streams).
You will now get the benefits of a faster protocol parser just by adding a couple of lines of code:
```php
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array(
'tcp' => 'Predis\Connection\PhpiredisConnection',
'unix' => 'Predis\Connection\PhpiredisConnection',
),
));
```
As simple as it is, nothing will really change in the way you use the library in your application. So,
how fast is it now? There are not much improvements for inline or short bulk replies (e.g. _SET_ or
_GET_), but the speed for parsing multi-bulk replies is now on par with phpredis:
Using Predis with a phpiredis-based connection to fetch 30000 keys using _KEYS *_:
0.035 seconds from a local Redis instance
0.047 seconds from a remote Redis instance
### If I need to install a C extension to get better performances, why not using phpredis? ###
Good question. Generically speaking, if you need absolute uber-speed using localhost instances of Redis
and you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you
do not need any kind of extensibility or guaranteed backwards compatibility with different versions of
Redis (Predis currently supports from 1.2 up to 2.6, and even the current development version), then
using __phpredis__ can make sense for you. Otherwise, Predis is perfect for the job. __phpiredis__
can give you a nice speed bump, but using it is not mandatory.
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009-2010 Daniele Alessandri
Copyright (c) 2009-2013 Daniele Alessandri
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
-141
View File
@@ -1,141 +0,0 @@
# Predis #
## About ##
Predis is a flexible and feature-complete PHP client library for the Redis key-value
database. It currently 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.
- a backport to PHP 5.2.x for those who can not upgrade their environment yet
(it admittedly has a lower priority compared to the mainline library, although we
try to keep the two versions aligned as much as possible).
Please refer to the TODO file to see which issues are still pending and what is due
to be implemented soon in Predis.
## Main features ##
- Full support for Redis 2.0. 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).
- 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 ##
See the [official wiki](http://wiki.github.com/nrk/predis) of the project for a more
complete coverage of all the features available in Predis.
### Connecting to a local instance of Redis ###
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->set('library', 'predis');
$value = $redis->get('library');
### Pipelining multiple commands to a remote instance of Redis ##
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');
});
### Pipelining multiple commands to multiple instances of Redis (sharding) ##
Predis supports data sharding using consistent-hashing on keys on the client side.
Furthermore, a pipeline can be initialized on a cluster of redis instances in the
same exact way they are created on single connection. Sharding is still transparent
to the user:
$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");
}
});
### Definition and runtime registration of new commands on the client ###
Let's suppose Redis just added the support for a brand new feature associated
with a new command. If you want to start using the above mentioned new feature
right away without messing with Predis source code or waiting for it to find
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 {
public function getCommandId() { return 'NEWCMD'; }
}
$redis = new Predis\Client();
$redis->getProfile()->registerCommand('BrandNewRedisCommand', 'newcmd');
$redis->newcmd();
## Development ##
Predis is fully backed up by a test suite which tries to cover all the aspects of the
client library and the interaction of every single command with a Redis server. If you
want to work on Predis, it is highly recommended that you first run the test suite to
be sure that everything is OK, and report strange behaviours or bugs.
The recommended way to contribute to Predis is to fork the project on GitHub, fix or
add features on your newly created repository and then submit issues on the Predis
issue tracker with a link to your repository. Obviously, you can use any other Git
hosting provider of you preference. Diff patches will be accepted too, even though
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.
## Dependencies ##
- PHP >= 5.3.0 (for the mainline client library)
- PHP >= 5.2.6 (for the backported client library)
- PHPUnit (needed to run the test suite)
## Links ##
### Project ###
- [Source code](http://github.com/nrk/predis/)
- [Wiki](http://wiki.github.com/nrk/predis/)
- [Issue tracker](http://github.com/nrk/predis/issues)
### Related ###
- [Redis](http://code.google.com/p/redis/)
- [PHP](http://php.net/)
- [PHPUnit](http://www.phpunit.de/)
- [Git](http://git-scm.com/)
## Author ##
[Daniele Alessandri](mailto:suppakilla@gmail.com)
## Contributors ##
[Lorenzo Castelli](http://github.com/lcastelli)
## License ##
The code for Predis is distributed under the terms of the MIT license (see LICENSE).
+261
View File
@@ -0,0 +1,261 @@
# Predis #
Predis is a flexible and feature-complete PHP (>= 5.3) client library for the Redis key-value store.
The library does not require any additional extension loaded in PHP but it can be optionally paired
with the [phpiredis](https://github.com/nrk/phpiredis) C-based extension to lower the overhead of
serializing and parsing the Redis protocol. Predis is also available in an asynchronous fashion
through the experimental client provided by the [Predis\Async](http://github.com/nrk/predis-async)
library.
For a list of frequently asked questions about Predis see our [FAQ](FAQ.md).
More details are available on the [official wiki](http://wiki.github.com/nrk/predis) of the project.
## Main features ##
- Wide range of Redis versions supported (from __1.2__ to __2.6__ and unstable) using server profiles.
- Smart support for [redis-cluster](http://redis.io/topics/cluster-spec) (Redis >= 3.0).
- Client-side sharding via consistent hashing or custom distribution strategies.
- Support for master / slave replication configurations (write on master, read from slaves).
- Transparent key prefixing strategy capable of handling any command known that has keys in its arguments.
- Command pipelining on single and aggregated connections.
- Abstraction for Redis transactions (Redis >= 2.0) with support for CAS operations (Redis >= 2.2).
- Abstraction for Lua scripting (Redis >= 2.6) capable of automatically switching between `EVAL` and `EVALSHA`.
- Connections to Redis instances are lazily established upon the first call to a command by the client.
- Ability to connect to Redis using TCP/IP or UNIX domain sockets with support for persistent connections.
- Ability to specify alternative connection classes to use different types of network or protocol backends.
- Flexible system to define and register your own set of commands or server profiles to client instances.
## How to use Predis ##
Predis is available on [Packagist](http://packagist.org/packages/predis/predis) for an easy installation
using [Composer](http://packagist.org/about-composer). Composer helps you manage dependencies for your
projects and libraries without much hassle which makes it the preferred way to get up and running with
new applications. Alternatively, the library is available on our [own PEAR channel](http://pear.nrk.io)
for a more traditional installation via PEAR. Zip and tar.gz archives are also downloadable from GitHub
by browsing the list of [tagged releases](http://github.com/nrk/predis/tags).
### Loading the library ###
Predis relies on the autoloading features of PHP to load its files when needed and complies with the
[PSR-0 standard](http://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) which makes it
compatible with most of the major frameworks and libraries. Autoloading in your application is handled
automatically when managing the dependencies with Composer, but you can also leverage its own autoloader
class if you are going to use it in a project or script without any PSR-0 compliant autoloading facility:
```php
<?php
// prepend a base path if Predis is not present in your "include_path".
require 'Predis/Autoloader.php';
Predis\Autoloader::register();
```
It is possible to create a single [Phar](http://www.php.net/manual/en/intro.phar.php) archive from the
repository just by launching `bin/create-phar.php`. The generated Phar archive ships with a stub defining
an autoloader function for Predis, so you just need to require the Phar to be able to use the library.
Alternatively it is possible to generate a single PHP file that holds every class, just like older versions
of Predis, using `bin/create-single-file.php`. In this way you can load Predis in your scripts simply by
using functions such as `require` and `include`, but this practice is not encouraged.
### Connecting to Redis ###
By default Predis uses `127.0.0.1` and `6379` as the default host and port when creating a new client
instance without specifying any connection parameter:
```php
$redis = new Predis\Client();
$redis->set('foo', 'bar');
$value = $redis->get('foo');
```
It is possible to specify the various connection parameters using URI strings or named arrays:
```php
$redis = new Predis\Client('tcp://10.0.0.1:6379');
// is equivalent to:
$redis = new Predis\Client(array(
'scheme' => 'tcp',
'host' => '10.0.0.1',
'port' => 6379,
));
```
### Pipelining commands to multiple instances of Redis with client-side sharding ###
Pipelining helps with performances when there is the need to send many commands to a server in one go.
Furthermore, pipelining works transparently even on aggregated connections. To achieve this, Predis
supports client-side sharding using consistent-hashing on keys while clustered connections are supported
natively by the client class.
```php
$redis = new Predis\Client(array(
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");
}
});
```
### Multiple and customizable connection backends ###
Predis can optionally use different connection backends to connect to Redis. Two of them leverage
the [phpiredis](http://github.com/nrk/phpiredis) C-based extension resulting in a major speed bump
especially when dealing with long multibulk replies, namely `Predis\Connection\PhpiredisConnection`
(the `socket` extension is also required) and `Predis\Connection\StreamPhpiredisConnection` (it
does not require additional extensions since it relies on PHP's native streams). Both of them can
connect to Redis using standard TCP/IP connections or UNIX domain sockets:
```php
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array(
'tcp' => 'Predis\Connection\PhpiredisConnection',
'unix' => 'Predis\Connection\PhpiredisStreamConnection',
)
));
```
Developers can also create their own connection backends to add support for new protocols, extend
existing ones or provide different implementations. Connection backend classes must implement
`Predis\Connection\SingleConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
```php
class MyConnectionClass implements Predis\Connection\SingleConnectionInterface
{
// implementation goes here
}
// Let Predis automatically use your own class to handle connections identified by the tcp scheme.
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array('tcp' => 'MyConnectionClass')
));
```
For a more in-depth insight on how to create new connection backends you can look at the actual
implementation of the classes contained in the `Predis\Connection` namespace.
### Defining and registering new commands on the client at runtime ###
Let's suppose Redis just added the support for a brand new feature associated with a new command. If
you want to start using the above mentioned new feature right away without messing with Predis source
code or waiting for it to find 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:
```php
class BrandNewRedisCommand extends Predis\Command\AbstractCommand
{
public function getId()
{
return 'NEWCMD';
}
}
$redis = new Predis\Client();
$redis->getProfile()->defineCommand('newcmd', 'BrandNewRedisCommand');
$redis->newcmd();
```
### Abstraction for handling Lua scripts as plain Redis commands ###
A scripted command in Predis is an abstraction for [Lua scripting](http://redis.io/commands/eval)
with Redis >= 2.6 that allows to use a Lua script as if it was a plain Redis command registered
in the server profile being used by the client instance. Internally, scripted commands use
[EVALSHA](http://redis.io/commands/evalsha) to refer to a Lua script by its SHA1 hash in order
to save bandwidth, but they are capable of falling back to [EVAL](http://redis.io/commands/eval)
when needed:
```php
class ListPushRandomValue extends Predis\Command\ScriptedCommand
{
public function getKeysCount()
{
return 1;
}
public function getScript()
{
return
<<<LUA
math.randomseed(ARGV[1])
local rnd = tostring(math.random())
redis.call('lpush', KEYS[1], rnd)
return rnd
LUA;
}
}
$client = new Predis\Client();
$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue');
$value = $client->lpushrand('random_values', $seed = mt_rand());
```
## Test suite ##
__ATTENTION__: Do not ever run the test suite shipped with Predis against instances of Redis running in
production environments or containing data you are interested in!
Predis has a comprehensive test suite covering every aspect of the library. The suite performs integration
tests against a running instance of Redis (>= 2.4.0 is required) to verify the correct behaviour of the
implementation of each command and automatically skips commands not defined in the selected version of
Redis. If you do not have Redis up and running, integration tests can be disabled. By default, the test
suite is configured to execute integration tests using the server profile for Redis v2.4 (which is the
current stable version of Redis). You can optionally run the suite against a Redis instance built from
the `unstable` branch with the development profile by changing the `REDIS_SERVER_VERSION` to `dev` in
the `phpunit.xml` file. More details on testing Predis can be found in [the tests README](tests/README.md).
Predis uses Travis CI for continuous integration. You can find the results of the test suite and the build
history [on its project page](http://travis-ci.org/nrk/predis).
## Dependencies ##
- PHP >= 5.3.2
- PHPUnit >= 3.5.0 (needed to run the test suite)
## Links ##
### Project ###
- [Source code](http://github.com/nrk/predis/)
- [Wiki](http://wiki.github.com/nrk/predis/)
- [Issue tracker](http://github.com/nrk/predis/issues)
- [PEAR channel](http://pear.nrk.io)
### Related ###
- [Redis](http://redis.io/)
- [PHP](http://php.net/)
- [PHPUnit](http://www.phpunit.de/)
- [Git](http://git-scm.com/)
## Author ##
- [Daniele Alessandri](mailto:suppakilla@gmail.com) ([twitter](http://twitter.com/JoL1hAHN))
## Contributors ##
- [Lorenzo Castelli](http://github.com/lcastelli)
- [Jordi Boggiano](http://github.com/Seldaek) ([twitter](http://twitter.com/seldaek))
- [Sebastian Waisbrot](http://github.com/seppo0010) ([twitter](http://twitter.com/seppo0010))
for his past work on extending [phpiredis](http://github.com/nrk/phpiredis) for Predis.
## License ##
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
-7
View File
@@ -1,7 +0,0 @@
* 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
+1 -1
View File
@@ -1 +1 @@
0.6.0
0.8.4
+14
View File
@@ -0,0 +1,14 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/lib/Predis/Autoloader.php';
Predis\Autoloader::register();
+71
View File
@@ -0,0 +1,71 @@
#!/usr/bin/env php
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// -------------------------------------------------------------------------- //
// In order to be able to execute this script to create a Phar archive of Predis,
// the Phar module must be loaded and the "phar.readonly" directive php.ini must
// be set to "off". You can change the values in the $options array to customize
// the creation of the Phar archive to better suit your needs.
// -------------------------------------------------------------------------- //
$options = array(
'name' => 'predis',
'project_path' => __DIR__ . '/../lib/',
'compression' => Phar::NONE,
'append_version' => true,
);
function getPharFilename($options)
{
$filename = $options['name'];
// NOTE: do not consider "append_version" with Phar compression do to a bug in
// Phar::compress() when renaming phar archives containing dots in their name.
if ($options['append_version'] && $options['compression'] === Phar::NONE) {
$versionFile = @fopen(__DIR__ . '/../VERSION', 'r');
if ($versionFile === false) {
throw new Exception("Could not locate the VERSION file.");
}
$version = trim(fgets($versionFile));
fclose($versionFile);
$filename .= "_$version";
}
return "$filename.phar";
}
function getPharStub($options)
{
return <<<EOSTUB
<?php
Phar::mapPhar('predis.phar');
spl_autoload_register(function (\$class) {
if (strpos(\$class, 'Predis\\\\') === 0) {
\$file = 'phar://predis.phar/'.strtr(\$class, '\\\', '/').'.php';
if (file_exists(\$file)) {
require \$file;
return true;
}
}
});
__HALT_COMPILER();
EOSTUB;
}
// -------------------------------------------------------------------------- //
$phar = new Phar(getPharFilename($options));
$phar->compress($options['compression']);
$phar->setStub(getPharStub($options));
$phar->buildFromDirectory($options['project_path']);
+606
View File
@@ -0,0 +1,606 @@
#!/usr/bin/env php
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// -------------------------------------------------------------------------- //
// This script can be used to automatically glue all the .php files of Predis
// into a single monolithic script file that can be used without an autoloader,
// just like the other previous versions of the library.
//
// Much of its complexity is due to the fact that we cannot simply join PHP
// files, but namespaces and classes definitions must follow a precise order
// when dealing with subclassing and inheritance.
//
// The current implementation is pretty naïve, but it should do for now.
// -------------------------------------------------------------------------- //
class CommandLine
{
public static function getOptions()
{
$parameters = array(
's:' => 'source:',
'o:' => 'output:',
'e:' => 'exclude:',
'E:' => 'exclude-classes:',
);
$getops = getopt(implode(array_keys($parameters)), $parameters);
$options = array(
'source' => __DIR__ . "/../lib/",
'output' => PredisFile::NS_ROOT . '.php',
'exclude' => array(),
);
foreach ($getops as $option => $value) {
switch ($option) {
case 's':
case 'source':
$options['source'] = $value;
break;
case 'o':
case 'output':
$options['output'] = $value;
break;
case 'E':
case 'exclude-classes':
$options['exclude'] = @file($value, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: $value;
break;
case 'e':
case 'exclude':
$options['exclude'] = is_array($value) ? $value : array($value);
break;
}
}
return $options;
}
}
class PredisFile
{
const NS_ROOT = 'Predis';
private $namespaces;
public function __construct()
{
$this->namespaces = array();
}
public static function from($libraryPath, Array $exclude = array())
{
$nsroot = self::NS_ROOT;
$predisFile = new PredisFile();
$libIterator = new RecursiveDirectoryIterator("$libraryPath$nsroot");
foreach (new RecursiveIteratorIterator($libIterator) as $classFile)
{
if (!$classFile->isFile()) {
continue;
}
$namespace = strtr(str_replace($libraryPath, '', $classFile->getPath()), '/', '\\');
if (in_array(sprintf('%s\\%s', $namespace, $classFile->getBasename('.php')), $exclude)) {
continue;
}
$phpNamespace = $predisFile->getNamespace($namespace);
if ($phpNamespace === false) {
$phpNamespace = new PhpNamespace($namespace);
$predisFile->addNamespace($phpNamespace);
}
$phpClass = new PhpClass($phpNamespace, $classFile);
}
return $predisFile;
}
public function addNamespace(PhpNamespace $namespace)
{
if (isset($this->namespaces[(string)$namespace])) {
throw new InvalidArgumentException("Duplicated namespace");
}
$this->namespaces[(string)$namespace] = $namespace;
}
public function getNamespaces()
{
return $this->namespaces;
}
public function getNamespace($namespace)
{
if (!isset($this->namespaces[$namespace])) {
return false;
}
return $this->namespaces[$namespace];
}
public function getClassByFQN($classFqn)
{
if (($nsLastPos = strrpos($classFqn, '\\')) !== false) {
$namespace = $this->getNamespace(substr($classFqn, 0, $nsLastPos));
if ($namespace === false) {
return null;
}
$className = substr($classFqn, $nsLastPos + 1);
return $namespace->getClass($className);
}
return null;
}
private function calculateDependencyScores(&$classes, $fqn)
{
if (!isset($classes[$fqn])) {
$classes[$fqn] = 0;
}
$classes[$fqn] += 1;
if (($phpClass = $this->getClassByFQN($fqn)) === null) {
throw new RuntimeException(
"Cannot found the class $fqn which is required by other subclasses. Are you missing a file?"
);
}
foreach ($phpClass->getDependencies() as $fqn) {
$this->calculateDependencyScores($classes, $fqn);
}
}
private function getDependencyScores()
{
$classes = array();
foreach ($this->getNamespaces() as $phpNamespace) {
foreach ($phpNamespace->getClasses() as $phpClass) {
$this->calculateDependencyScores($classes, $phpClass->getFQN());
}
}
return $classes;
}
private function getOrderedNamespaces($dependencyScores)
{
$namespaces = array_fill_keys(array_unique(
array_map(
function ($fqn) { return PhpNamespace::extractName($fqn); },
array_keys($dependencyScores)
)
), 0);
foreach ($dependencyScores as $classFqn => $score) {
$namespaces[PhpNamespace::extractName($classFqn)] += $score;
}
arsort($namespaces);
return array_keys($namespaces);
}
private function getOrderedClasses(PhpNamespace $phpNamespace, $classes)
{
$nsClassesFQNs = array_map(function ($cl) { return $cl->getFQN(); }, $phpNamespace->getClasses());
$nsOrderedClasses = array();
foreach ($nsClassesFQNs as $nsClassFQN) {
$nsOrderedClasses[$nsClassFQN] = $classes[$nsClassFQN];
}
arsort($nsOrderedClasses);
return array_keys($nsOrderedClasses);
}
public function getPhpCode()
{
$buffer = array("<?php\n\n", PhpClass::LICENSE_HEADER, "\n\n");
$classes = $this->getDependencyScores();
$namespaces = $this->getOrderedNamespaces($classes);
foreach ($namespaces as $namespace) {
$phpNamespace = $this->getNamespace($namespace);
// generate namespace directive
$buffer[] = $phpNamespace->getPhpCode();
$buffer[] = "\n";
// generate use directives
$useDirectives = $phpNamespace->getUseDirectives();
if (count($useDirectives) > 0) {
$buffer[] = $useDirectives->getPhpCode();
$buffer[] = "\n";
}
// generate classes bodies
$nsClasses = $this->getOrderedClasses($phpNamespace, $classes);
foreach ($nsClasses as $classFQN) {
$buffer[] = $this->getClassByFQN($classFQN)->getPhpCode();
$buffer[] = "\n\n";
}
$buffer[] = "/* " . str_repeat("-", 75) . " */";
$buffer[] = "\n\n";
}
return implode($buffer);
}
public function saveTo($outputFile)
{
// TODO: add more sanity checks
if ($outputFile === null || $outputFile === '') {
throw new InvalidArgumentException('You must specify a valid output file');
}
file_put_contents($outputFile, $this->getPhpCode());
}
}
class PhpNamespace implements IteratorAggregate
{
private $namespace;
private $classes;
public function __construct($namespace)
{
$this->namespace = $namespace;
$this->classes = array();
$this->useDirectives = new PhpUseDirectives($this);
}
public static function extractName($fqn)
{
$nsSepLast = strrpos($fqn, '\\');
if ($nsSepLast === false) {
return $fqn;
}
$ns = substr($fqn, 0, $nsSepLast);
return $ns !== '' ? $ns : null;
}
public function addClass(PhpClass $class)
{
$this->classes[$class->getName()] = $class;
}
public function getClass($className)
{
if (isset($this->classes[$className])) {
return $this->classes[$className];
}
}
public function getClasses()
{
return array_values($this->classes);
}
public function getIterator()
{
return new \ArrayIterator($this->getClasses());
}
public function getUseDirectives()
{
return $this->useDirectives;
}
public function getPhpCode()
{
return "namespace $this->namespace;\n";
}
public function __toString()
{
return $this->namespace;
}
}
class PhpUseDirectives implements Countable, IteratorAggregate
{
private $use;
private $aliases;
private $namespace;
public function __construct(PhpNamespace $namespace)
{
$this->use = array();
$this->aliases = array();
$this->namespace = $namespace;
}
public function add($use, $as = null)
{
if (in_array($use, $this->use)) {
return;
}
$this->use[] = $use;
$this->aliases[$as ?: PhpClass::extractName($use)] = $use;
}
public function getList()
{
return $this->use;
}
public function getIterator()
{
return new \ArrayIterator($this->getList());
}
public function getPhpCode()
{
$reducer = function ($str, $use) {
return $str .= "use $use;\n";
};
return array_reduce($this->getList(), $reducer, '');
}
public function getNamespace()
{
return $this->namespace;
}
public function getFQN($className)
{
if (($nsSepFirst = strpos($className, '\\')) === false) {
if (isset($this->aliases[$className])) {
return $this->aliases[$className];
}
return (string)$this->getNamespace() . "\\$className";
}
if ($nsSepFirst != 0) {
throw new InvalidArgumentException("Partially qualified names are not supported");
}
return $className;
}
public function count()
{
return count($this->use);
}
}
class PhpClass
{
const LICENSE_HEADER = <<<LICENSE
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
LICENSE;
private $namespace;
private $file;
private $body;
private $implements;
private $extends;
private $name;
public function __construct(PhpNamespace $namespace, SplFileInfo $classFile)
{
$this->namespace = $namespace;
$this->file = $classFile;
$this->implements = array();
$this->extends = array();
$this->extractData();
$namespace->addClass($this);
}
public static function extractName($fqn)
{
$nsSepLast = strrpos($fqn, '\\');
if ($nsSepLast === false) {
return $fqn;
}
return substr($fqn, $nsSepLast + 1);
}
private function extractData()
{
$useDirectives = $this->getNamespace()->getUseDirectives();
$useExtractor = function ($m) use ($useDirectives) {
$useDirectives->add(($namespacedPath = $m[1]));
};
$classBuffer = stream_get_contents(fopen($this->getFile()->getPathname(), 'r'));
$classBuffer = str_replace(self::LICENSE_HEADER, '', $classBuffer);
$classBuffer = preg_replace('/<\?php\s?\\n\s?/', '', $classBuffer);
$classBuffer = preg_replace('/\s?\?>\n?/ms', '', $classBuffer);
$classBuffer = preg_replace('/namespace\s+[\w\d_\\\\]+;\s?/', '', $classBuffer);
$classBuffer = preg_replace_callback('/use\s+([\w\d_\\\\]+)(\s+as\s+.*)?;\s?\n?/', $useExtractor, $classBuffer);
$this->body = trim($classBuffer);
$this->extractHierarchy();
}
private function extractHierarchy()
{
$implements = array();
$extends = array();
$extractor = function ($iterator, $callback) {
$className = '';
$iterator->seek($iterator->key() + 1);
while ($iterator->valid()) {
$token = $iterator->current();
if (is_string($token)) {
if (preg_match('/\s?,\s?/', $token)) {
$callback(trim($className));
$className = '';
} else if ($token == '{') {
$callback(trim($className));
return;
}
}
switch ($token[0]) {
case T_NS_SEPARATOR:
$className .= '\\';
break;
case T_STRING:
$className .= $token[1];
break;
case T_IMPLEMENTS:
case T_EXTENDS:
$callback(trim($className));
$iterator->seek($iterator->key() - 1);
return;
}
$iterator->next();
}
};
$tokens = token_get_all("<?php\n" . trim($this->getPhpCode()));
$iterator = new ArrayIterator($tokens);
while ($iterator->valid()) {
$token = $iterator->current();
if (is_string($token)) {
$iterator->next();
continue;
}
switch ($token[0]) {
case T_CLASS:
case T_INTERFACE:
$iterator->seek($iterator->key() + 2);
$tk = $iterator->current();
$this->name = $tk[1];
break;
case T_IMPLEMENTS:
$extractor($iterator, function ($fqn) use (&$implements) {
$implements[] = $fqn;
});
break;
case T_EXTENDS:
$extractor($iterator, function ($fqn) use (&$extends) {
$extends[] = $fqn;
});
break;
}
$iterator->next();
}
$this->implements = $this->guessFQN($implements);
$this->extends = $this->guessFQN($extends);
}
public function guessFQN($classes)
{
$useDirectives = $this->getNamespace()->getUseDirectives();
return array_map(array($useDirectives, 'getFQN'), $classes);
}
public function getImplementedInterfaces($all = false)
{
if ($all) {
return $this->implements;
}
return array_filter(
$this->implements,
function ($cn) { return strpos($cn, 'Predis\\') === 0; }
);
}
public function getExtendedClasses($all = false)
{
if ($all) {
return $this->extemds;
}
return array_filter(
$this->extends,
function ($cn) { return strpos($cn, 'Predis\\') === 0; }
);
}
public function getDependencies($all = false)
{
return array_merge(
$this->getImplementedInterfaces($all),
$this->getExtendedClasses($all)
);
}
public function getNamespace()
{
return $this->namespace;
}
public function getFile()
{
return $this->file;
}
public function getName()
{
return $this->name;
}
public function getFQN()
{
return (string)$this->getNamespace() . '\\' . $this->name;
}
public function getPhpCode()
{
return $this->body;
}
public function __toString()
{
return "class " . $this->getName() . '{ ... }';
}
}
/* -------------------------------------------------------------------------- */
$options = CommandLine::getOptions();
$predisFile = PredisFile::from($options['source'], $options['exclude']);
$predisFile->saveTo($options['output']);
+273
View File
@@ -0,0 +1,273 @@
#!/usr/bin/env php
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// -------------------------------------------------------------------------- //
// This script can be used to automatically generate a file with the scheleton
// of a test case to test a Redis command by specifying the name of the class
// in the Predis\Command namespace (only classes in this namespace are valid).
// For example, to generate a test case for SET (which is represented by the
// Predis\Command\StringSet class):
//
// $ ./bin/generate-command-test.php --class=StringSet
//
// Here is a list of optional arguments:
//
// --realm: each command has its own realm (commands that operate on strings,
// lists, sets and such) but while this realm is usually inferred from the name
// of the specified class, sometimes it can be useful to override it with a
// custom one.
//
// --output: write the generated test case to the specified path instead of
// the default one.
//
// --overwrite: pre-existing test files are not overwritten unless this option
// is explicitly specified.
// -------------------------------------------------------------------------- //
use Predis\Command\CommandInterface;
use Predis\Command\PrefixableCommandInterface;
class CommandTestCaseGenerator
{
private $options;
public function __construct(Array $options)
{
if (!isset($options['class'])) {
throw new RuntimeException("Missing 'class' option.");
}
$this->options = $options;
}
public static function fromCommandLine()
{
$parameters = array(
'c:' => 'class:',
'r::' => 'realm::',
'o::' => 'output::',
'x::' => 'overwrite::'
);
$getops = getopt(implode(array_keys($parameters)), $parameters);
$options = array(
'overwrite' => false,
'tests' => __DIR__.'/../tests',
);
foreach ($getops as $option => $value) {
switch ($option) {
case 'c':
case 'class':
$options['class'] = $value;
break;
case 'r':
case 'realm':
$options['realm'] = $value;
break;
case 'o':
case 'output':
$options['output'] = $value;
break;
case 'x':
case 'overwrite':
$options['overwrite'] = true;
break;
}
}
if (!isset($options['class'])) {
throw new RuntimeException("Missing 'class' option.");
}
$options['fqn'] = "Predis\\Command\\{$options['class']}";
$options['path'] = "Predis/Command/{$options['class']}.php";
$source = __DIR__.'/../lib/'.$options['path'];
if (!file_exists($source)) {
throw new RuntimeException("Cannot find class file for {$options['fqn']} in $source.");
}
if (!isset($options['output'])) {
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', 'Test.php', $options['path']));
}
return new self($options);
}
protected function getTestRealm()
{
if (isset($this->options['realm'])) {
if (!$this->options['realm']) {
throw new RuntimeException('Invalid value for realm has been sepcified (empty).');
}
return $this->options['realm'];
}
$fqnParts = explode('\\', $this->options['fqn']);
$class = array_pop($fqnParts);
list($realm,) = preg_split('/([[:upper:]][[:lower:]]+)/', $class, 2, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
return strtolower($realm);
}
public function generate()
{
$reflection = new ReflectionClass($class = $this->options['fqn']);
if (!$reflection->isInstantiable()) {
throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed.");
}
if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) {
throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface.");
}
$instance = $reflection->newInstance();
$buffer = $this->getTestCaseBuffer($instance);
return $buffer;
}
public function save()
{
$options = $this->options;
if (file_exists($options['output']) && !$options['overwrite']) {
throw new RuntimeException("File {$options['output']} already exist. Specify the --overwrite option to overwrite the existing file.");
}
file_put_contents($options['output'], $this->generate());
}
protected function getTestCaseBuffer(CommandInterface $instance)
{
$id = $instance->getId();
$fqn = get_class($instance);
$fqnParts = explode('\\', $fqn);
$class = array_pop($fqnParts) . "Test";
$realm = $this->getTestRealm();
$buffer =<<<PHP
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
use \PHPUnit_Framework_TestCase as StandardTestCase;
/**
* @group commands
* @group realm-$realm
*/
class $class extends CommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand()
{
return '$fqn';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId()
{
return '$id';
}
/**
* @group disconnected
*/
public function testFilterArguments()
{
\$this->markTestIncomplete('This test has not been implemented yet.');
\$arguments = array(/* add arguments */);
\$expected = array(/* add arguments */);
\$command = \$this->getCommand();
\$command->setArguments(\$arguments);
\$this->assertSame(\$expected, \$command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse()
{
\$this->markTestIncomplete('This test has not been implemented yet.');
\$raw = null;
\$expected = null;
\$command = \$this->getCommand();
\$this->assertSame(\$expected, \$command->parseResponse(\$raw));
}
PHP;
if ($instance instanceof PrefixableCommandInterface) {
$buffer .=<<<PHP
/**
* @group disconnected
*/
public function testPrefixKeys()
{
\$this->markTestIncomplete('This test has not been implemented yet.');
\$arguments = array(/* add arguments */);
\$expected = array(/* add arguments */);
\$command = \$this->getCommandWithArgumentsArray(\$arguments);
\$command->prefixKeys('prefix:');
\$this->assertSame(\$expected, \$command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
\$command = \$this->getCommand();
\$command->prefixKeys('prefix:');
\$this->assertSame(array(), \$command->getArguments());
}
PHP;
}
return "$buffer}\n";
}
}
// ------------------------------------------------------------------------- //
require __DIR__.'/../autoload.php';
$generator = CommandTestCaseGenerator::fromCommandLine();
$generator->save();
+25
View File
@@ -0,0 +1,25 @@
{
"name": "predis/predis",
"type": "library",
"description": "Flexible and feature-complete PHP client library for Redis",
"keywords": ["nosql", "redis", "predis"],
"homepage": "http://github.com/nrk/predis",
"license": "MIT",
"authors": [
{
"name": "Daniele Alessandri",
"email": "suppakilla@gmail.com",
"homepage": "http://clorophilla.net"
}
],
"require": {
"php": ">=5.3.2"
},
"suggest": {
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
"ext-curl": "Allows access to Webdis when paired with phpiredis"
},
"autoload": {
"psr-0": {"Predis": "lib/"}
}
}
+58 -29
View File
@@ -1,60 +1,89 @@
<?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.
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class NaiveDistributionStrategy
implements Predis\Distribution\IDistributionStrategy {
require 'SharedConfigurations.php';
private $_nodes, $_nodesCount;
// 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 Predis\Distribution\DistributionStrategyInterface.
public function __constructor() {
$this->_nodes = array();
$this->_nodesCount = 0;
use Predis\Connection\PredisCluster;
use Predis\Cluster\Distribution\DistributionStrategyInterface;
use Predis\Cluster\Hash\HashGeneratorInterface;
class NaiveDistributionStrategy implements DistributionStrategyInterface, HashGeneratorInterface
{
private $nodes;
private $nodesCount;
public function __construct()
{
$this->nodes = array();
$this->nodesCount = 0;
}
public function add($node, $weight = null) {
$this->_nodes[] = $node;
$this->_nodesCount++;
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) {
public function remove($node)
{
$this->nodes = array_filter($this->nodes, function ($n) use ($node) {
return $n !== $node;
});
$this->_nodesCount = count($this->_nodes);
$this->nodesCount = count($this->nodes);
}
public function get($key) {
$count = $this->_nodesCount;
if ($count === 0) {
public function get($key)
{
if (0 === $count = $this->nodesCount) {
throw new RuntimeException('No connections');
}
return $this->_nodes[$count > 1 ? abs(crc32($key) % $count) : 0];
return $this->nodes[$count > 1 ? abs($key % $count) : 0];
}
public function generateKey($value) {
public function hash($value)
{
return crc32($value);
}
public function getHashGenerator()
{
return $this;
}
}
$options = array(
'key_distribution' => new NaiveDistributionStrategy(),
'cluster' => function () {
$distributor = new NaiveDistributionStrategy();
$cluster = new PredisCluster($distributor);
return $cluster;
},
);
$redis = new Predis\Client($multiple_servers, $options);
$client = 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");
$client->set("key:$i", str_pad($i, 4, '0', 0));
$client->get("key:$i");
}
$server1 = $redis->getClientFor('first')->info();
$server2 = $redis->getClientFor('second')->info();
$server1 = $client->getClientFor('first')->info();
$server2 = $client->getClientFor('second')->info();
printf("Server '%s' has %d keys while server '%s' has %d keys.\n",
printf("Server '%s' has %d keys while server '%s' has %d keys.\n",
'first', $server1['db15']['keys'], 'second', $server2['db15']['keys']
);
?>
+78
View File
@@ -0,0 +1,78 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
/*
This is a basic example on how to use the Predis\DispatcherLoop class.
To see this example in action you can just use redis-cli and publish some
messages to the 'events' and 'control' channel, e.g.:
./redis-cli
PUBLISH events first
PUBLISH events second
PUBLISH events third
PUBLISH control terminate_dispatcher
*/
// Create a client and disable r/w timeout on the socket
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
// Create a Predis\DispatcherLoop instance and attach a bunch of callbacks.
$dispatcher = new Predis\PubSub\DispatcherLoop($client);
// Demonstrate how to use a callable class as a callback for Predis\DispatcherLoop.
class EventsListener implements Countable
{
private $events;
public function __construct()
{
$this->events = array();
}
public function count()
{
return count($this->events);
}
public function getEvents()
{
return $this->events;
}
public function __invoke($payload)
{
$this->events[] = $payload;
}
}
// Attach our callable class to the dispatcher.
$dispatcher->attachCallback('events', ($events = new EventsListener()));
// Attach a function to control the dispatcher loop termination with a message.
$dispatcher->attachCallback('control', function ($payload) use ($dispatcher) {
if ($payload === 'terminate_dispatcher') {
$dispatcher->stop();
}
});
// Run the dispatcher loop until the callback attached to the 'control' channel
// receives 'terminate_dispatcher' as a message.
$dispatcher->run();
// Display our achievements!
echo "We received {$events->count()} messages!\n";
// Say goodbye :-)
$info = $client->info();
print_r("Goodbye from Redis v{$info['redis_version']}!\n");
+37
View File
@@ -0,0 +1,37 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
// Predis ships with a KeyPrefixProcessor class that is used to transparently
// prefix each key before sending commands to Redis, even for complex commands
// such as SORT, ZUNIONSTORE and ZINTERSTORE. Key prefixes are useful to create
// user-level namespaces for you keyspace, thus eliminating the need for separate
// logical databases.
$client = new Predis\Client($single_server, array('prefix' => 'nrk:'));
$client->mset(array('foo' => 'bar', 'lol' => 'wut'));
var_dump($client->mget('foo', 'lol'));
/*
array(2) {
[0]=> string(3) "bar"
[1]=> string(3) "wut"
}
*/
var_dump($client->keys('*'));
/*
array(2) {
[0]=> string(7) "nrk:foo"
[1]=> string(7) "nrk:lol"
}
*/
+52
View File
@@ -0,0 +1,52 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
// Predis supports master / slave replication scenarios where write operations are
// performed on the master server and read operations are executed against one of
// the slaves. The behaviour of commands or EVAL scripts can be customized at will.
// As soon as a write operation is performed, all the subsequent requests (reads
// or writes) will be served by the master server.
//
// This example must be executed with the second Redis server acting as the slave
// of the first one using the SLAVEOF command.
//
$parameters = array(
'tcp://127.0.0.1:6379?database=15&alias=master',
'tcp://127.0.0.1:6380?database=15&alias=slave',
);
$options = array('replication' => true);
$client = new Predis\Client($parameters, $options);
// Read operation.
$exists = $client->exists('foo') ? 'yes' : 'no';
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Does 'foo' exist on {$current->alias}? $exists.\n";
// Write operation.
$client->set('foo', 'bar');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Now 'foo' has been set to 'bar' on {$current->alias}!\n";
// Read operation.
$bar = $client->get('foo');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "We just fetched 'foo' from {$current->alias} and its value is '$bar'.\n";
/* OUTPUT:
Does 'foo' exist on slave? yes.
Now 'foo' has been set to 'bar' on master!
We just fetched 'foo' from master and its value is 'bar'.
*/
@@ -0,0 +1,84 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
// Predis allows to set Lua scripts as read-only operations in the context of
// replication. This works for both EVAL and EVALSHA and also for the client-side
// abstraction built upon them (Predis\Command\ScriptedCommand). This example
// shows a slightly more complex configuration that injects a new scripted command
// in the server profile used by the new client instance and marks it marks it as
// a read-only operation for replication so that it will be executed on slaves.
use Predis\Command\ScriptedCommand;
use Predis\Connection\MasterSlaveReplication;
use Predis\Profile\ServerProfile;
use Predis\Replication\ReplicationStrategy;
// ------------------------------------------------------------------------- //
// Define a new scripted command that returns all the fields
// of a variable number of hashes with a single roundtrip.
class HashMultipleGetAll extends ScriptedCommand {
const BODY = <<<EOS
local hashes = {}
for _, key in pairs(KEYS) do
table.insert(hashes, key)
table.insert(hashes, redis.call('hgetall', key))
end
return hashes
EOS;
public function getScript() {
return self::BODY;
}
}
// ------------------------------------------------------------------------- //
$parameters = array(
'tcp://127.0.0.1:6379/?alias=master',
'tcp://127.0.0.1:6380/?alias=slave',
);
$options = array(
'profile' => function ($options, $option) {
$profile = $options->getDefault($option);
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
return $profile;
},
'replication' => function ($options) {
$strategy = new ReplicationStrategy();
$strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
$replication = new MasterSlaveReplication($strategy);
return $replication;
},
);
// ------------------------------------------------------------------------- //
$client = new Predis\Client($parameters, $options);
// Execute the following commands on the master server using redis-cli:
// $ ./redis-cli HMSET metavars foo bar hoge piyo
// $ ./redis-cli HMSET servers master host1 slave host2
$hashes = $client->hmgetall('metavars', 'servers');
$replication = $client->getConnection();
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionById('slave');
echo "Is still on slave? ", $stillOnSlave ? 'YES' : 'NO', "!\n";
var_export($hashes);
+44
View File
@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
// This is a basic example on how to use the Predis\MonitorContext class.
// You can use redis-cli to send commands to the same Redis instance your client is
// connected to, and then type "ECHO QUIT_MONITOR" in redis-cli when you want to
// exit the monitor loop and terminate this script in a graceful way.
// Create a client and disable r/w timeout on the socket.
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
// Use only one instance of DateTime, we will update the timestamp later.
$timestamp = new DateTime();
foreach (($monitor = $client->monitor()) as $event) {
$timestamp->setTimestamp((int) $event->timestamp);
// If we notice a ECHO command with the message QUIT_MONITOR, we close the
// monitor context and then break the loop.
if ($event->command === 'ECHO' && $event->arguments === '"QUIT_MONITOR"') {
echo "Exiting the monitor loop...\n";
$monitor->closeContext();
break;
}
echo "* Received {$event->command} on DB {$event->database} at {$timestamp->format(DateTime::W3C)}\n";
if (isset($event->arguments)) {
echo " Arguments: {$event->arguments}\n";
}
}
// Say goodbye :-)
$info = $client->info();
print_r("Goodbye from Redis v{$info['redis_version']}!\n");
+56
View File
@@ -0,0 +1,56 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
// Operations such as LRANGE, ZRANGE and others can potentially generate replies
// containing a huge number of items. In some corner cases, such replies might
// end up exhausting the maximum allowed memory allocated for a PHP process.
// Multibulk iterators can be handy because they allow you to stream multibulk
// replies using plain old PHP iterators, making it possible to iterate them with
// a classic `foreach` loop and avoiding to consume an excessive amount of memory.
//
// PS: please note that multibulk iterators are supported only by the standard
// connection backend class (Predis\Connection\StreamConnection) and not the
// phpiredis-based one (Predis\Connection\PhpiredisConnection).
// Create a client and force the connection to use iterable multibulk responses.
$client = new Predis\Client($single_server + array('iterable_multibulk' => true));
// Prepare an hash with some fields and their respective values.
$client->hmset('metavars', array('foo' => 'bar', 'hoge' => 'piyo', 'lol' => 'wut'));
// By default multibulk iterators iterate over the reply as a list of items...
foreach ($client->hgetall('metavars') as $index => $item) {
echo "[$index] $item\n";
}
/* OUTPUT:
[0] foo
[1] bar
[2] hoge
[3] piyo
[4] lol
[5] wut
*/
// ... but certain multibulk replies are better represented as lists of tuples.
foreach ($client->hgetall('metavars')->asTuple() as $index => $kv) {
list($key, $value) = $kv;
echo "[$index] $key => $value\n";
}
/* OUTPUT:
[0] foo => bar
[1] hoge => piyo
[2] lol => wut
*/
+51
View File
@@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require '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, $key)
{
$element = null;
$options = array(
'cas' => true, // Initialize with support for CAS operations
'watch' => $key, // 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.
);
$client->multiExec($options, function ($tx) use ($key, &$element) {
@list($element) = $tx->zrange($key, 0, 0);
if (isset($element)) {
$tx->multi(); // With CAS, MULTI *must* be explicitly invoked.
$tx->zrem($key, $element);
}
});
return $element;
}
$client = new Predis\Client($single_server);
$zpopped = zpop($client, 'zset');
echo isset($zpopped) ? "ZPOPed $zpopped" : "Nothing to ZPOP!", "\n";
+18 -9
View File
@@ -1,20 +1,30 @@
<?php
require_once 'SharedConfigurations.php';
// redis can set keys and their relative values in one go
// using MSET, then the same values can be retrieved with
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
// redis can set keys and their relative values in one go
// using MSET, then the same values can be retrieved with
// a single command using MGET.
$mkv = array(
'usr:0001' => 'First user',
'usr:0002' => 'Second user',
'usr:0003' => 'Third user'
'usr:0002' => 'Second user',
'usr:0003' => 'Third user'
);
$redis = new Predis\Client($single_server);
$client = new Predis\Client($single_server);
$redis->mset($mkv);
$retval = $redis->mget(array_keys($mkv));
$client->mset($mkv);
$retval = $client->mget(array_keys($mkv));
print_r($retval);
@@ -26,4 +36,3 @@ Array
[2] => Third user
)
*/
?>
@@ -1,12 +1,22 @@
<?php
require_once 'SharedConfigurations.php';
// When you have a whole set of consecutive commands to send to
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
// 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);
$client = new Predis\Client($single_server);
$replies = $redis->pipeline(function($pipe) {
$replies = $client->pipeline(function ($pipe) {
$pipe->ping();
$pipe->flushdb();
$pipe->incrby('counter', 10);
@@ -29,10 +39,9 @@ Array
[5] => 40
[6] => Array
(
[0] =>
[0] =>
[1] => 40
)
)
*/
?>
+22 -15
View File
@@ -1,20 +1,29 @@
<?php
require_once 'SharedConfigurations.php';
// Redis 2.0 features new commands that allow clients to subscribe for
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require '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));
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
// Initialize a new pubsub context
$pubsub = $redis->pubSubContext();
$pubsub = $client->pubSub();
// Subscribe to your channels
$pubsub->subscribe('control_channel');
$pubsub->subscribe('notifications');
$pubsub->subscribe('control_channel', 'notifications');
// Start processing the pubsup messages. Open a terminal and use redis-cli
// 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
@@ -23,17 +32,16 @@ foreach ($pubsub as $message) {
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 {
} else {
echo "Received an unrecognized command: {$message->payload}.\n";
}
}
else {
} else {
echo "Received the following message from {$message->channel}:\n",
" {$message->payload}\n\n";
}
@@ -41,12 +49,11 @@ foreach ($pubsub as $message) {
}
}
// Always unset the pubsub context instance when you are done! The
// class destructor will take care of cleanups and prevent protocol
// 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();
$info = $client->info();
print_r("Goodbye from Redis v{$info['redis_version']}!\n");
?>
+66
View File
@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
// This example will not work with versions of Redis < 2.6.
//
// Additionally to the EVAL command defined in the current development profile, the new
// Predis\Command\ScriptedCommand base class can be used to build an higher abstraction
// for our "scripted" commands so that they will appear just like any other command on
// the client-side. This is a quick example used to implement INCREX.
use Predis\Command\ScriptedCommand;
class IncrementExistingKeysBy extends ScriptedCommand
{
public function getKeysCount()
{
// Tell Predis to use all the arguments but the last one as arguments
// for KEYS. The last one will be used to populate ARGV.
return -1;
}
public function getScript()
{
return
<<<LUA
local cmd, insert = redis.call, table.insert
local increment, results = ARGV[1], { }
for idx, key in ipairs(KEYS) do
if cmd('exists', key) == 1 then
insert(results, idx, cmd('incrby', key, increment))
else
insert(results, idx, false)
end
end
return results
LUA;
}
}
$client = new Predis\Client($single_server);
$client->getProfile()->defineCommand('increxby', 'IncrementExistingKeysBy');
$client->mset('foo', 10, 'foobar', 100);
var_export($client->increxby('foo', 'foofoo', 'foobar', 50));
/*
array (
0 => 60,
1 => NULL,
2 => 150,
)
*/
+39
View File
@@ -0,0 +1,39 @@
<?php
require 'SharedConfigurations.php';
// This example demonstrates how to leverage Predis to save PHP sessions on Redis.
//
// The value of `session.gc_maxlifetime` in `php.ini` will be used by default as the
// the TTL for keys holding session data on Redis, but this value can be overridden
// when creating the session handler instance with the `gc_maxlifetime` option.
//
// Note that this class needs PHP >= 5.4 but can be used on PHP 5.3 if a polyfill for
// SessionHandlerInterface (see http://www.php.net/class.sessionhandlerinterface.php)
// is provided either by you or an external package like `symfony/http-foundation`.
if (!interface_exists('SessionHandlerInterface')) {
die("ATTENTION: the session handler implemented by Predis needs PHP >= 5.4.0 or a polyfill ".
"for \SessionHandlerInterface either provided by you or an external package.\n");
}
// Instantiate a new client just like you would normally do. We'll prefix our session keys here.
$client = new Predis\Client($single_server, array('prefix' => 'sessions:'));
// Set `gc_maxlifetime` so that a session will be expired after 5 seconds since last access.
$handler = new Predis\Session\SessionHandler($client, array('gc_maxlifetime' => 5));
// Register our session handler (it uses `session_set_save_handler()` internally).
$handler->register();
// Set a fixed session ID just for the sake of our example.
session_id('example_session_id');
session_start();
if (isset($_SESSION['foo'])) {
echo "Session has `foo` set to {$_SESSION['foo']}\n";
} else {
$_SESSION['foo'] = $value = mt_rand();
echo "Empty session, `foo` has been set with $value\n";
}
+13 -4
View File
@@ -1,9 +1,19 @@
<?php
require_once '../lib/Predis.php';
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/../autoload.php';
$single_server = array(
'host' => '127.0.0.1',
'port' => 6379,
'host' => '127.0.0.1',
'port' => 6379,
'database' => 15
);
@@ -21,4 +31,3 @@ $multiple_servers = array(
'alias' => 'second',
),
);
?>
+88
View File
@@ -0,0 +1,88 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
use Predis\Command\CommandInterface;
use Predis\Connection\StreamConnection;
class SimpleDebuggableConnection extends StreamConnection
{
private $tstart = 0;
private $debugBuffer = array();
public function connect()
{
$this->tstart = microtime(true);
parent::connect();
}
private function storeDebug(CommandInterface $command, $direction)
{
$firtsArg = $command->getArgument(0);
$timestamp = round(microtime(true) - $this->tstart, 4);
$debug = $command->getId();
$debug .= isset($firtsArg) ? " $firtsArg " : ' ';
$debug .= "$direction $this";
$debug .= " [{$timestamp}s]";
$this->debugBuffer[] = $debug;
}
public function writeCommand(CommandInterface $command)
{
parent::writeCommand($command);
$this->storeDebug($command, '->');
}
public function readResponse(CommandInterface $command)
{
$reply = parent::readResponse($command);
$this->storeDebug($command, '<-');
return $reply;
}
public function getDebugBuffer()
{
return $this->debugBuffer;
}
}
$options = array(
'connections' => array(
'tcp' => 'SimpleDebuggableConnection',
),
);
$client = new Predis\Client($single_server, $options);
$client->set('foo', 'bar');
$client->get('foo');
$client->info();
print_r($client->getConnection()->getDebugBuffer());
/* OUTPUT:
Array
(
[0] => SELECT 15 -> 127.0.0.1:6379 [0.0008s]
[1] => SELECT 15 <- 127.0.0.1:6379 [0.0012s]
[2] => SET foo -> 127.0.0.1:6379 [0.0014s]
[3] => SET foo <- 127.0.0.1:6379 [0.0014s]
[4] => GET foo -> 127.0.0.1:6379 [0.0016s]
[5] => GET foo <- 127.0.0.1:6379 [0.0018s]
[6] => INFO -> 127.0.0.1:6379 [0.002s]
[7] => INFO <- 127.0.0.1:6379 [0.0025s]
)
*/
+16 -7
View File
@@ -1,16 +1,25 @@
<?php
require_once 'SharedConfigurations.php';
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require 'SharedConfigurations.php';
// simple set and get scenario
$redis = new Predis\Client($single_server);
$client = new Predis\Client($single_server);
$redis->set('library', 'predis');
$retval = $redis->get('library');
$client->set('library', 'predis');
$retval = $client->get('library');
print_r($retval);
var_dump($retval);
/* OUTPUT
predis
string(6) "predis"
*/
?>
-2731
View File
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
/**
* Implements a lightweight PSR-0 compliant autoloader.
*
* @author Eric Naeseth <eric@thumbtack.com>
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class Autoloader
{
private $directory;
private $prefix;
private $prefixLength;
/**
* @param string $baseDirectory Base directory where the source files are located.
*/
public function __construct($baseDirectory = __DIR__)
{
$this->directory = $baseDirectory;
$this->prefix = __NAMESPACE__ . '\\';
$this->prefixLength = strlen($this->prefix);
}
/**
* Registers the autoloader class with the PHP SPL autoloader.
*
* @param boolean $prepend Prepend the autoloader on the stack instead of appending it.
*/
public static function register($prepend = false)
{
spl_autoload_register(array(new self, 'autoload'), true, $prepend);
}
/**
* Loads a class from a file using its fully qualified name.
*
* @param string $className Fully qualified name of a class.
*/
public function autoload($className)
{
if (0 === strpos($className, $this->prefix)) {
$parts = explode('\\', substr($className, $this->prefixLength));
$filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
if (is_file($filepath)) {
require($filepath);
}
}
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
use Predis\Command\CommandInterface;
/**
* Defines the interface of a basic client object or abstraction that
* can send commands to Redis.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface BasicClientInterface
{
/**
* Executes the specified Redis command.
*
* @param CommandInterface $command A Redis command.
* @return mixed
*/
public function executeCommand(CommandInterface $command);
}
+436
View File
@@ -0,0 +1,436 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
use Predis\Command\CommandInterface;
use Predis\Command\ScriptedCommand;
use Predis\Connection\AggregatedConnectionInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\ConnectionFactoryInterface;
use Predis\Monitor\MonitorContext;
use Predis\Option\ClientOptions;
use Predis\Option\ClientOptionsInterface;
use Predis\Pipeline\PipelineContext;
use Predis\Profile\ServerProfile;
use Predis\PubSub\PubSubContext;
use Predis\Transaction\MultiExecContext;
/**
* Main class that exposes the most high-level interface to interact with Redis.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class Client implements ClientInterface
{
const VERSION = '0.8.4';
private $options;
private $profile;
private $connection;
/**
* Initializes a new client with optional connection parameters and client options.
*
* @param mixed $parameters Connection parameters for one or multiple servers.
* @param mixed $options Options that specify certain behaviours for the client.
*/
public function __construct($parameters = null, $options = null)
{
$this->options = $this->filterOptions($options);
$this->profile = $this->options->profile;
$this->connection = $this->initializeConnection($parameters);
}
/**
* Creates an instance of Predis\Option\ClientOptions from various types of
* arguments (string, array, Predis\Profile\ServerProfile) or returns the
* passed object if it is an instance of Predis\Option\ClientOptions.
*
* @param mixed $options Client options.
* @return ClientOptions
*/
protected function filterOptions($options)
{
if (!isset($options)) {
return new ClientOptions();
}
if (is_array($options)) {
return new ClientOptions($options);
}
if ($options instanceof ClientOptionsInterface) {
return $options;
}
throw new \InvalidArgumentException("Invalid type for client options");
}
/**
* Initializes one or multiple connection (cluster) objects from various
* types of arguments (string, array) or returns the passed object if it
* implements Predis\Connection\ConnectionInterface.
*
* @param mixed $parameters Connection parameters or instance.
* @return ConnectionInterface
*/
protected function initializeConnection($parameters)
{
if ($parameters instanceof ConnectionInterface) {
return $parameters;
}
if (is_array($parameters) && isset($parameters[0])) {
$options = $this->options;
$replication = isset($options->replication) && $options->replication;
$connection = $options->{$replication ? 'replication' : 'cluster'};
return $options->connections->createAggregated($connection, $parameters);
}
if (is_callable($parameters)) {
$connection = call_user_func($parameters, $this->options);
if (!$connection instanceof ConnectionInterface) {
throw new \InvalidArgumentException(
'Callable parameters must return instances of Predis\Connection\ConnectionInterface'
);
}
return $connection;
}
return $this->options->connections->create($parameters);
}
/**
* {@inheritdoc}
*/
public function getProfile()
{
return $this->profile;
}
/**
* {@inheritdoc}
*/
public function getOptions()
{
return $this->options;
}
/**
* Returns the connection factory object used by the client.
*
* @return ConnectionFactoryInterface
*/
public function getConnectionFactory()
{
return $this->options->connections;
}
/**
* Returns a new instance of a client for the specified connection when the
* client is connected to a cluster. The new instance will use the same
* options of the original client.
*
* @return Client
*/
public function getClientFor($connectionID)
{
if (!$connection = $this->getConnectionById($connectionID)) {
throw new \InvalidArgumentException("Invalid connection ID: '$connectionID'");
}
return new static($connection, $this->options);
}
/**
* Opens the connection to the server.
*/
public function connect()
{
$this->connection->connect();
}
/**
* Disconnects from the server.
*/
public function disconnect()
{
$this->connection->disconnect();
}
/**
* Disconnects from the server.
*
* This method is an alias of disconnect().
*/
public function quit()
{
$this->disconnect();
}
/**
* Checks if the underlying connection is connected to Redis.
*
* @return Boolean True means that the connection is open.
* False means that the connection is closed.
*/
public function isConnected()
{
return $this->connection->isConnected();
}
/**
* {@inheritdoc}
*/
public function getConnection()
{
return $this->connection;
}
/**
* Retrieves a single connection out of an aggregated connections instance.
*
* @param string $connectionId Index or alias of the connection.
* @return Connection\SingleConnectionInterface
*/
public function getConnectionById($connectionId)
{
if (!$this->connection instanceof AggregatedConnectionInterface) {
throw new NotSupportedException('Retrieving connections by ID is supported only when using aggregated connections');
}
return $this->connection->getConnectionById($connectionId);
}
/**
* Dynamically invokes a Redis command with the specified arguments.
*
* @param string $method The name of a Redis command.
* @param array $arguments The arguments for the command.
* @return mixed
*/
public function __call($method, $arguments)
{
$command = $this->profile->createCommand($method, $arguments);
$response = $this->connection->executeCommand($command);
if ($response instanceof ResponseObjectInterface) {
if ($response instanceof ResponseErrorInterface) {
$response = $this->onResponseError($command, $response);
}
return $response;
}
return $command->parseResponse($response);
}
/**
* {@inheritdoc}
*/
public function createCommand($method, $arguments = array())
{
return $this->profile->createCommand($method, $arguments);
}
/**
* {@inheritdoc}
*/
public function executeCommand(CommandInterface $command)
{
$response = $this->connection->executeCommand($command);
if ($response instanceof ResponseObjectInterface) {
if ($response instanceof ResponseErrorInterface) {
$response = $this->onResponseError($command, $response);
}
return $response;
}
return $command->parseResponse($response);
}
/**
* Handles -ERR responses returned by Redis.
*
* @param CommandInterface $command The command that generated the error.
* @param ResponseErrorInterface $response The error response instance.
* @return mixed
*/
protected function onResponseError(CommandInterface $command, ResponseErrorInterface $response)
{
if ($command instanceof ScriptedCommand && $response->getErrorType() === 'NOSCRIPT') {
$eval = $this->createCommand('eval');
$eval->setRawArguments($command->getEvalArguments());
$response = $this->executeCommand($eval);
if (!$response instanceof ResponseObjectInterface) {
$response = $command->parseResponse($response);
}
return $response;
}
if ($this->options->exceptions) {
throw new ServerException($response->getMessage());
}
return $response;
}
/**
* Calls the specified initializer method on $this with 0, 1 or 2 arguments.
*
* TODO: Invert $argv and $initializer.
*
* @param array $argv Arguments for the initializer.
* @param string $initializer The initializer method.
* @return mixed
*/
private function sharedInitializer($argv, $initializer)
{
switch (count($argv)) {
case 0:
return $this->$initializer();
case 1:
list($arg0) = $argv;
return is_array($arg0) ? $this->$initializer($arg0) : $this->$initializer(null, $arg0);
case 2:
list($arg0, $arg1) = $argv;
return $this->$initializer($arg0, $arg1);
default:
return $this->$initializer($this, $argv);
}
}
/**
* Creates a new pipeline context and returns it, or returns the results of
* a pipeline executed inside the optionally provided callable object.
*
* @param mixed $arg,... Options for the context, a callable object, or both.
* @return PipelineContext|array
*/
public function pipeline(/* arguments */)
{
return $this->sharedInitializer(func_get_args(), 'initPipeline');
}
/**
* Pipeline context initializer.
*
* @param array $options Options for the context.
* @param mixed $callable Optional callable object used to execute the context.
* @return PipelineContext|array
*/
protected function initPipeline(Array $options = null, $callable = null)
{
$executor = isset($options['executor']) ? $options['executor'] : null;
if (is_callable($executor)) {
$executor = call_user_func($executor, $this, $options);
}
$pipeline = new PipelineContext($this, $executor);
$replies = $this->pipelineExecute($pipeline, $callable);
return $replies;
}
/**
* Executes a pipeline context when a callable object is passed.
*
* @param array $options Options of the context initialization.
* @param mixed $callable Optional callable object used to execute the context.
* @return PipelineContext|array
*/
private function pipelineExecute(PipelineContext $pipeline, $callable)
{
return isset($callable) ? $pipeline->execute($callable) : $pipeline;
}
/**
* Creates a new transaction context and returns it, or returns the results of
* a transaction executed inside the optionally provided callable object.
*
* @param mixed $arg,... Options for the context, a callable object, or both.
* @return MultiExecContext|array
*/
public function multiExec(/* arguments */)
{
return $this->sharedInitializer(func_get_args(), 'initMultiExec');
}
/**
* Transaction context initializer.
*
* @param array $options Options for the context.
* @param mixed $callable Optional callable object used to execute the context.
* @return MultiExecContext|array
*/
protected function initMultiExec(Array $options = null, $callable = null)
{
$transaction = new MultiExecContext($this, $options ?: array());
return isset($callable) ? $transaction->execute($callable) : $transaction;
}
/**
* Creates a new Publish / Subscribe context and returns it, or executes it
* inside the optionally provided callable object.
*
* @param mixed $arg,... Options for the context, a callable object, or both.
* @return MultiExecContext|array
*/
public function pubSub(/* arguments */)
{
return $this->sharedInitializer(func_get_args(), 'initPubSub');
}
/**
* Publish / Subscribe context initializer.
*
* @param array $options Options for the context.
* @param mixed $callable Optional callable object used to execute the context.
* @return PubSubContext
*/
protected function initPubSub(Array $options = null, $callable = null)
{
$pubsub = new PubSubContext($this, $options);
if (!isset($callable)) {
return $pubsub;
}
foreach ($pubsub as $message) {
if (call_user_func($callable, $pubsub, $message) === false) {
$pubsub->closeContext();
}
}
}
/**
* Returns a new monitor context.
*
* @return MonitorContext
*/
public function monitor()
{
return new MonitorContext($this);
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
/**
* Exception class that identifies client-side errors.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ClientException extends PredisException
{
}
+66
View File
@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
use Predis\Connection\ConnectionInterface;
use Predis\Option\ClientOptionsInterface;
use Predis\Profile\ServerProfileInterface;
/**
* Interface defining the most important parts needed to create an
* high-level Redis client object that can interact with other
* building blocks of Predis.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface ClientInterface extends BasicClientInterface
{
/**
* Returns the server profile used by the client.
*
* @return ServerProfileInterface
*/
public function getProfile();
/**
* Returns the client options specified upon initialization.
*
* @return ClientOptionsInterface
*/
public function getOptions();
/**
* Opens the connection to the server.
*/
public function connect();
/**
* Disconnects from the server.
*/
public function disconnect();
/**
* Returns the underlying connection instance.
*
* @return ConnectionInterface
*/
public function getConnection();
/**
* Creates a new instance of the specified Redis command.
*
* @param string $method The name of a Redis command.
* @param array $arguments The arguments for the command.
* @return Command\CommandInterface
*/
public function createCommand($method, $arguments = array());
}
@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster;
use Predis\Command\CommandInterface;
/**
* Interface for classes defining the strategy used to calculate an hash
* out of keys extracted from supported commands.
*
* This is mostly useful to support clustering via client-side sharding.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface CommandHashStrategyInterface
{
/**
* Returns the hash for the given command using the specified algorithm, or null
* if the command cannot be hashed.
*
* @param CommandInterface $command Command to be hashed.
* @return int
*/
public function getHash(CommandInterface $command);
/**
* Returns the hash for the given key using the specified algorithm.
*
* @param string $key Key to be hashed.
* @return string
*/
public function getKeyHash($key);
}
@@ -0,0 +1,52 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Distribution;
use Predis\Cluster\Hash\HashGeneratorInterface;
/**
* A distributor implements the logic to automatically distribute
* keys among several nodes for client-side sharding.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface DistributionStrategyInterface
{
/**
* Adds a node to the distributor with an optional weight.
*
* @param mixed $node Node object.
* @param int $weight Weight for the node.
*/
public function add($node, $weight = null);
/**
* Removes a node from the distributor.
*
* @param mixed $node Node object.
*/
public function remove($node);
/**
* Gets a node from the distributor using the computed hash of a key.
*
* @return mixed
*/
public function get($key);
/**
* Returns the underlying hash generator instance.
*
* @return HashGeneratorInterface
*/
public function getHashGenerator();
}
@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Distribution;
/**
* Exception class that identifies empty rings.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class EmptyRingException extends \Exception
{
}
@@ -0,0 +1,246 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Distribution;
use Predis\Cluster\Hash\HashGeneratorInterface;
/**
* This class implements an hashring-based distributor that uses the same
* algorithm of memcache to distribute keys in a cluster using client-side
* sharding.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
* @author Lorenzo Castelli <lcastelli@gmail.com>
*/
class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
{
const DEFAULT_REPLICAS = 128;
const DEFAULT_WEIGHT = 100;
private $ring;
private $ringKeys;
private $ringKeysCount;
private $replicas;
private $nodeHashCallback;
private $nodes = array();
/**
* @param int $replicas Number of replicas in the ring.
* @param mixed $nodeHashCallback Callback returning the string used to calculate the hash of a node.
*/
public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null)
{
$this->replicas = $replicas;
$this->nodeHashCallback = $nodeHashCallback;
}
/**
* Adds a node to the ring with an optional weight.
*
* @param mixed $node Node object.
* @param int $weight Weight for the node.
*/
public function add($node, $weight = null)
{
// In case of collisions in the hashes of the nodes, the node added
// last wins, thus the order in which nodes are added is significant.
$this->nodes[] = array('object' => $node, 'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT);
$this->reset();
}
/**
* {@inheritdoc}
*/
public function remove($node)
{
// A node is removed by resetting the ring so that it's recreated from
// scratch, in order to reassign possible hashes with collisions to the
// right node according to the order in which they were added in the
// first place.
for ($i = 0; $i < count($this->nodes); ++$i) {
if ($this->nodes[$i]['object'] === $node) {
array_splice($this->nodes, $i, 1);
$this->reset();
break;
}
}
}
/**
* Resets the distributor.
*/
private function reset()
{
unset(
$this->ring,
$this->ringKeys,
$this->ringKeysCount
);
}
/**
* Returns the initialization status of the distributor.
*
* @return Boolean
*/
private function isInitialized()
{
return isset($this->ringKeys);
}
/**
* Calculates the total weight of all the nodes in the distributor.
*
* @return int
*/
private function computeTotalWeight()
{
$totalWeight = 0;
foreach ($this->nodes as $node) {
$totalWeight += $node['weight'];
}
return $totalWeight;
}
/**
* Initializes the distributor.
*/
private function initialize()
{
if ($this->isInitialized()) {
return;
}
if (!$this->nodes) {
throw new EmptyRingException('Cannot initialize empty hashring');
}
$this->ring = array();
$totalWeight = $this->computeTotalWeight();
$nodesCount = count($this->nodes);
foreach ($this->nodes as $node) {
$weightRatio = $node['weight'] / $totalWeight;
$this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio);
}
ksort($this->ring, SORT_NUMERIC);
$this->ringKeys = array_keys($this->ring);
$this->ringKeysCount = count($this->ringKeys);
}
/**
* Implements the logic needed to add a node to the hashring.
*
* @param array $ring Source hashring.
* @param mixed $node Node object to be added.
* @param int $totalNodes Total number of nodes.
* @param int $replicas Number of replicas in the ring.
* @param float $weightRatio Weight ratio for the node.
*/
protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
{
$nodeObject = $node['object'];
$nodeHash = $this->getNodeHash($nodeObject);
$replicas = (int) round($weightRatio * $totalNodes * $replicas);
for ($i = 0; $i < $replicas; $i++) {
$key = crc32("$nodeHash:$i");
$ring[$key] = $nodeObject;
}
}
/**
* {@inheritdoc}
*/
protected function getNodeHash($nodeObject)
{
if ($this->nodeHashCallback === null) {
return (string) $nodeObject;
}
return call_user_func($this->nodeHashCallback, $nodeObject);
}
/**
* Calculates the hash for the specified value.
*
* @param string $value Input value.
* @return int
*/
public function hash($value)
{
return crc32($value);
}
/**
* {@inheritdoc}
*/
public function get($key)
{
return $this->ring[$this->getNodeKey($key)];
}
/**
* Calculates the corrisponding key of a node distributed in the hashring.
*
* @param int $key Computed hash of a key.
* @return int
*/
private function getNodeKey($key)
{
$this->initialize();
$ringKeys = $this->ringKeys;
$upper = $this->ringKeysCount - 1;
$lower = 0;
while ($lower <= $upper) {
$index = ($lower + $upper) >> 1;
$item = $ringKeys[$index];
if ($item > $key) {
$upper = $index - 1;
} else if ($item < $key) {
$lower = $index + 1;
} else {
return $item;
}
}
return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)];
}
/**
* Implements a strategy to deal with wrap-around errors during binary searches.
*
* @param int $upper
* @param int $lower
* @param int $ringKeysCount
* @return int
*/
protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
{
// Binary search for the last item in ringkeys with a value less or
// equal to the key. If no such item exists, return the last item.
return $upper >= 0 ? $upper : $ringKeysCount - 1;
}
/**
* {@inheritdoc}
*/
public function getHashGenerator()
{
return $this;
}
}
@@ -0,0 +1,70 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Distribution;
/**
* This class implements an hashring-based distributor that uses the same
* algorithm of libketama to distribute keys in a cluster using client-side
* sharding.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
* @author Lorenzo Castelli <lcastelli@gmail.com>
*/
class KetamaPureRing extends HashRing
{
const DEFAULT_REPLICAS = 160;
/**
* @param mixed $nodeHashCallback Callback returning the string used to calculate the hash of a node.
*/
public function __construct($nodeHashCallback = null)
{
parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback);
}
/**
* {@inheritdoc}
*/
protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
{
$nodeObject = $node['object'];
$nodeHash = $this->getNodeHash($nodeObject);
$replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4));
for ($i = 0; $i < $replicas; $i++) {
$unpackedDigest = unpack('V4', md5("$nodeHash-$i", true));
foreach ($unpackedDigest as $key) {
$ring[$key] = $nodeObject;
}
}
}
/**
* {@inheritdoc}
*/
public function hash($value)
{
$hash = unpack('V', md5($value, true));
return $hash[1];
}
/**
* {@inheritdoc}
*/
protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
{
// Binary search for the first item in _ringkeys with a value greater
// or equal to the key. If no such item exists, return the first item.
return $lower < $ringKeysCount ? $lower : 0;
}
}
@@ -0,0 +1,72 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Hash;
/**
* This class implements the CRC-CCITT-16 algorithm used by redis-cluster.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class CRC16HashGenerator implements HashGeneratorInterface
{
private static $CCITT_16 = array(
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
);
/**
* {@inheritdoc}
*/
public function hash($value)
{
// CRC-CCITT-16 algorithm
$crc = 0;
$CCITT_16 = self::$CCITT_16;
$strlen = strlen($value);
for ($i = 0; $i < $strlen; $i++) {
$crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF;
}
return $crc;
}
}
@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Hash;
/**
* A generator of node keys implements the logic used to calculate the hash of
* a key to distribute the respective operations among nodes.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface HashGeneratorInterface
{
/**
* Generates an hash that is used by the distributor algorithm
*
* @param string $value Value used to generate the hash.
* @return int
*/
public function hash($value);
}
@@ -0,0 +1,385 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\Command\CommandInterface;
use Predis\Command\ScriptedCommand;
/**
* Default class used by Predis for client-side sharding to calculate
* hashes out of keys of supported commands.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PredisClusterHashStrategy implements CommandHashStrategyInterface
{
private $commands;
private $hashGenerator;
/**
* @param HashGeneratorInterface $hashGenerator Hash generator instance.
*/
public function __construct(HashGeneratorInterface $hashGenerator)
{
$this->commands = $this->getDefaultCommands();
$this->hashGenerator = $hashGenerator;
}
/**
* Returns the default map of supported commands with their handlers.
*
* @return array
*/
protected function getDefaultCommands()
{
$keyIsFirstArgument = array($this, 'getKeyFromFirstArgument');
$keysAreAllArguments = array($this, 'getKeyFromAllArguments');
return array(
/* commands operating on the key space */
'EXISTS' => $keyIsFirstArgument,
'DEL' => $keysAreAllArguments,
'TYPE' => $keyIsFirstArgument,
'EXPIRE' => $keyIsFirstArgument,
'EXPIREAT' => $keyIsFirstArgument,
'PERSIST' => $keyIsFirstArgument,
'PEXPIRE' => $keyIsFirstArgument,
'PEXPIREAT' => $keyIsFirstArgument,
'TTL' => $keyIsFirstArgument,
'PTTL' => $keyIsFirstArgument,
'SORT' => $keyIsFirstArgument, // TODO
'DUMP' => $keyIsFirstArgument,
'RESTORE' => $keyIsFirstArgument,
/* commands operating on string values */
'APPEND' => $keyIsFirstArgument,
'DECR' => $keyIsFirstArgument,
'DECRBY' => $keyIsFirstArgument,
'GET' => $keyIsFirstArgument,
'GETBIT' => $keyIsFirstArgument,
'MGET' => $keysAreAllArguments,
'SET' => $keyIsFirstArgument,
'GETRANGE' => $keyIsFirstArgument,
'GETSET' => $keyIsFirstArgument,
'INCR' => $keyIsFirstArgument,
'INCRBY' => $keyIsFirstArgument,
'SETBIT' => $keyIsFirstArgument,
'SETEX' => $keyIsFirstArgument,
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
'SETNX' => $keyIsFirstArgument,
'SETRANGE' => $keyIsFirstArgument,
'STRLEN' => $keyIsFirstArgument,
'SUBSTR' => $keyIsFirstArgument,
'BITOP' => array($this, 'getKeyFromBitOp'),
'BITCOUNT' => $keyIsFirstArgument,
/* commands operating on lists */
'LINSERT' => $keyIsFirstArgument,
'LINDEX' => $keyIsFirstArgument,
'LLEN' => $keyIsFirstArgument,
'LPOP' => $keyIsFirstArgument,
'RPOP' => $keyIsFirstArgument,
'RPOPLPUSH' => $keysAreAllArguments,
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'),
'LPUSH' => $keyIsFirstArgument,
'LPUSHX' => $keyIsFirstArgument,
'RPUSH' => $keyIsFirstArgument,
'RPUSHX' => $keyIsFirstArgument,
'LRANGE' => $keyIsFirstArgument,
'LREM' => $keyIsFirstArgument,
'LSET' => $keyIsFirstArgument,
'LTRIM' => $keyIsFirstArgument,
/* commands operating on sets */
'SADD' => $keyIsFirstArgument,
'SCARD' => $keyIsFirstArgument,
'SDIFF' => $keysAreAllArguments,
'SDIFFSTORE' => $keysAreAllArguments,
'SINTER' => $keysAreAllArguments,
'SINTERSTORE' => $keysAreAllArguments,
'SUNION' => $keysAreAllArguments,
'SUNIONSTORE' => $keysAreAllArguments,
'SISMEMBER' => $keyIsFirstArgument,
'SMEMBERS' => $keyIsFirstArgument,
'SPOP' => $keyIsFirstArgument,
'SRANDMEMBER' => $keyIsFirstArgument,
'SREM' => $keyIsFirstArgument,
/* commands operating on sorted sets */
'ZADD' => $keyIsFirstArgument,
'ZCARD' => $keyIsFirstArgument,
'ZCOUNT' => $keyIsFirstArgument,
'ZINCRBY' => $keyIsFirstArgument,
'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
'ZRANGE' => $keyIsFirstArgument,
'ZRANGEBYSCORE' => $keyIsFirstArgument,
'ZRANK' => $keyIsFirstArgument,
'ZREM' => $keyIsFirstArgument,
'ZREMRANGEBYRANK' => $keyIsFirstArgument,
'ZREMRANGEBYSCORE' => $keyIsFirstArgument,
'ZREVRANGE' => $keyIsFirstArgument,
'ZREVRANGEBYSCORE' => $keyIsFirstArgument,
'ZREVRANK' => $keyIsFirstArgument,
'ZSCORE' => $keyIsFirstArgument,
'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
/* commands operating on hashes */
'HDEL' => $keyIsFirstArgument,
'HEXISTS' => $keyIsFirstArgument,
'HGET' => $keyIsFirstArgument,
'HGETALL' => $keyIsFirstArgument,
'HMGET' => $keyIsFirstArgument,
'HMSET' => $keyIsFirstArgument,
'HINCRBY' => $keyIsFirstArgument,
'HINCRBYFLOAT' => $keyIsFirstArgument,
'HKEYS' => $keyIsFirstArgument,
'HLEN' => $keyIsFirstArgument,
'HSET' => $keyIsFirstArgument,
'HSETNX' => $keyIsFirstArgument,
'HVALS' => $keyIsFirstArgument,
/* scripting */
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
);
}
/**
* Returns the list of IDs for the supported commands.
*
* @return array
*/
public function getSupportedCommands()
{
return array_keys($this->commands);
}
/**
* Sets an handler for the specified command ID.
*
* The signature of the callback must have a single parameter
* of type Predis\Command\CommandInterface.
*
* When the callback argument is omitted or NULL, the previously
* associated handler for the specified command ID is removed.
*
* @param string $commandId The ID of the command to be handled.
* @param mixed $callback A valid callable object or NULL.
*/
public function setCommandHandler($commandId, $callback = null)
{
$commandId = strtoupper($commandId);
if (!isset($callback)) {
unset($this->commands[$commandId]);
return;
}
if (!is_callable($callback)) {
throw new \InvalidArgumentException("Callback must be a valid callable object or NULL");
}
$this->commands[$commandId] = $callback;
}
/**
* Extracts the key from the first argument of a command instance.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromFirstArgument(CommandInterface $command)
{
return $command->getArgument(0);
}
/**
* Extracts the key from a command with multiple keys only when all keys
* in the arguments array produce the same hash.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromAllArguments(CommandInterface $command)
{
$arguments = $command->getArguments();
if ($this->checkSameHashForKeys($arguments)) {
return $arguments[0];
}
}
/**
* Extracts the key from a command with multiple keys only when all keys
* in the arguments array produce the same hash.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromInterleavedArguments(CommandInterface $command)
{
$arguments = $command->getArguments();
$keys = array();
for ($i = 0; $i < count($arguments); $i += 2) {
$keys[] = $arguments[$i];
}
if ($this->checkSameHashForKeys($keys)) {
return $arguments[0];
}
}
/**
* Extracts the key from BLPOP and BRPOP commands.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromBlockingListCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
if ($this->checkSameHashForKeys(array_slice($arguments, 0, count($arguments) - 1))) {
return $arguments[0];
}
}
/**
* Extracts the key from BITOP command.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromBitOp(CommandInterface $command)
{
$arguments = $command->getArguments();
if ($this->checkSameHashForKeys(array_slice($arguments, 1, count($arguments)))) {
return $arguments[1];
}
}
/**
* Extracts the key from ZINTERSTORE and ZUNIONSTORE commands.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromZsetAggregationCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
$keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1]));
if ($this->checkSameHashForKeys($keys)) {
return $arguments[0];
}
}
/**
* Extracts the key from EVAL and EVALSHA commands.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromScriptingCommands(CommandInterface $command)
{
if ($command instanceof ScriptedCommand) {
$keys = $command->getKeys();
} else {
$keys = array_slice($args = $command->getArguments(), 2, $args[1]);
}
if ($keys && $this->checkSameHashForKeys($keys)) {
return $keys[0];
}
}
/**
* {@inheritdoc}
*/
public function getHash(CommandInterface $command)
{
$hash = $command->getHash();
if (!isset($hash) && isset($this->commands[$cmdID = $command->getId()])) {
$key = call_user_func($this->commands[$cmdID], $command);
if (isset($key)) {
$hash = $this->getKeyHash($key);
$command->setHash($hash);
}
}
return $hash;
}
/**
* {@inheritdoc}
*/
public function getKeyHash($key)
{
$key = $this->extractKeyTag($key);
$hash = $this->hashGenerator->hash($key);
return $hash;
}
/**
* Checks if the specified array of keys will generate the same hash.
*
* @param array $keys Array of keys.
* @return Boolean
*/
protected function checkSameHashForKeys(Array $keys)
{
if (!$count = count($keys)) {
return false;
}
$currentKey = $this->extractKeyTag($keys[0]);
for ($i = 1; $i < $count; $i++) {
$nextKey = $this->extractKeyTag($keys[$i]);
if ($currentKey !== $nextKey) {
return false;
}
$currentKey = $nextKey;
}
return true;
}
/**
* Returns only the hashable part of a key (delimited by "{...}"), or the
* whole key if a key tag is not found in the string.
*
* @param string $key A key.
* @return string
*/
protected function extractKeyTag($key)
{
if (false !== $start = strpos($key, '{')) {
if (false !== $end = strpos($key, '}', $start)) {
$key = substr($key, ++$start, $end - $start);
}
}
return $key;
}
}
@@ -0,0 +1,288 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster;
use Predis\Cluster\Hash\CRC16HashGenerator;
use Predis\Command\CommandInterface;
use Predis\Command\ScriptedCommand;
/**
* Default class used by Predis to calculate hashes out of keys of
* commands supported by redis-cluster.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class RedisClusterHashStrategy implements CommandHashStrategyInterface
{
private $commands;
private $hashGenerator;
/**
*
*/
public function __construct()
{
$this->commands = $this->getDefaultCommands();
$this->hashGenerator = new CRC16HashGenerator();
}
/**
* Returns the default map of supported commands with their handlers.
*
* @return array
*/
protected function getDefaultCommands()
{
$keyIsFirstArgument = array($this, 'getKeyFromFirstArgument');
return array(
/* commands operating on the key space */
'EXISTS' => $keyIsFirstArgument,
'DEL' => array($this, 'getKeyFromAllArguments'),
'TYPE' => $keyIsFirstArgument,
'EXPIRE' => $keyIsFirstArgument,
'EXPIREAT' => $keyIsFirstArgument,
'PERSIST' => $keyIsFirstArgument,
'PEXPIRE' => $keyIsFirstArgument,
'PEXPIREAT' => $keyIsFirstArgument,
'TTL' => $keyIsFirstArgument,
'PTTL' => $keyIsFirstArgument,
'SORT' => $keyIsFirstArgument, // TODO
/* commands operating on string values */
'APPEND' => $keyIsFirstArgument,
'DECR' => $keyIsFirstArgument,
'DECRBY' => $keyIsFirstArgument,
'GET' => $keyIsFirstArgument,
'GETBIT' => $keyIsFirstArgument,
'MGET' => array($this, 'getKeyFromAllArguments'),
'SET' => $keyIsFirstArgument,
'GETRANGE' => $keyIsFirstArgument,
'GETSET' => $keyIsFirstArgument,
'INCR' => $keyIsFirstArgument,
'INCRBY' => $keyIsFirstArgument,
'SETBIT' => $keyIsFirstArgument,
'SETEX' => $keyIsFirstArgument,
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
'SETNX' => $keyIsFirstArgument,
'SETRANGE' => $keyIsFirstArgument,
'STRLEN' => $keyIsFirstArgument,
'SUBSTR' => $keyIsFirstArgument,
'BITCOUNT' => $keyIsFirstArgument,
/* commands operating on lists */
'LINSERT' => $keyIsFirstArgument,
'LINDEX' => $keyIsFirstArgument,
'LLEN' => $keyIsFirstArgument,
'LPOP' => $keyIsFirstArgument,
'RPOP' => $keyIsFirstArgument,
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
'LPUSH' => $keyIsFirstArgument,
'LPUSHX' => $keyIsFirstArgument,
'RPUSH' => $keyIsFirstArgument,
'RPUSHX' => $keyIsFirstArgument,
'LRANGE' => $keyIsFirstArgument,
'LREM' => $keyIsFirstArgument,
'LSET' => $keyIsFirstArgument,
'LTRIM' => $keyIsFirstArgument,
/* commands operating on sets */
'SADD' => $keyIsFirstArgument,
'SCARD' => $keyIsFirstArgument,
'SISMEMBER' => $keyIsFirstArgument,
'SMEMBERS' => $keyIsFirstArgument,
'SPOP' => $keyIsFirstArgument,
'SRANDMEMBER' => $keyIsFirstArgument,
'SREM' => $keyIsFirstArgument,
/* commands operating on sorted sets */
'ZADD' => $keyIsFirstArgument,
'ZCARD' => $keyIsFirstArgument,
'ZCOUNT' => $keyIsFirstArgument,
'ZINCRBY' => $keyIsFirstArgument,
'ZRANGE' => $keyIsFirstArgument,
'ZRANGEBYSCORE' => $keyIsFirstArgument,
'ZRANK' => $keyIsFirstArgument,
'ZREM' => $keyIsFirstArgument,
'ZREMRANGEBYRANK' => $keyIsFirstArgument,
'ZREMRANGEBYSCORE' => $keyIsFirstArgument,
'ZREVRANGE' => $keyIsFirstArgument,
'ZREVRANGEBYSCORE' => $keyIsFirstArgument,
'ZREVRANK' => $keyIsFirstArgument,
'ZSCORE' => $keyIsFirstArgument,
/* commands operating on hashes */
'HDEL' => $keyIsFirstArgument,
'HEXISTS' => $keyIsFirstArgument,
'HGET' => $keyIsFirstArgument,
'HGETALL' => $keyIsFirstArgument,
'HMGET' => $keyIsFirstArgument,
'HMSET' => $keyIsFirstArgument,
'HINCRBY' => $keyIsFirstArgument,
'HINCRBYFLOAT' => $keyIsFirstArgument,
'HKEYS' => $keyIsFirstArgument,
'HLEN' => $keyIsFirstArgument,
'HSET' => $keyIsFirstArgument,
'HSETNX' => $keyIsFirstArgument,
'HVALS' => $keyIsFirstArgument,
/* scripting */
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
);
}
/**
* Returns the list of IDs for the supported commands.
*
* @return array
*/
public function getSupportedCommands()
{
return array_keys($this->commands);
}
/**
* Sets an handler for the specified command ID.
*
* The signature of the callback must have a single parameter
* of type Predis\Command\CommandInterface.
*
* When the callback argument is omitted or NULL, the previously
* associated handler for the specified command ID is removed.
*
* @param string $commandId The ID of the command to be handled.
* @param mixed $callback A valid callable object or NULL.
*/
public function setCommandHandler($commandId, $callback = null)
{
$commandId = strtoupper($commandId);
if (!isset($callback)) {
unset($this->commands[$commandId]);
return;
}
if (!is_callable($callback)) {
throw new \InvalidArgumentException("Callback must be a valid callable object or NULL");
}
$this->commands[$commandId] = $callback;
}
/**
* Extracts the key from the first argument of a command instance.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromFirstArgument(CommandInterface $command)
{
return $command->getArgument(0);
}
/**
* Extracts the key from a command that can accept multiple keys ensuring
* that only one key is actually specified to comply with redis-cluster.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromAllArguments(CommandInterface $command)
{
$arguments = $command->getArguments();
if (count($arguments) === 1) {
return $arguments[0];
}
}
/**
* Extracts the key from a command that can accept multiple keys ensuring
* that only one key is actually specified to comply with redis-cluster.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromInterleavedArguments(CommandInterface $command)
{
$arguments = $command->getArguments();
if (count($arguments) === 2) {
return $arguments[0];
}
}
/**
* Extracts the key from BLPOP and BRPOP commands ensuring that only one key
* is actually specified to comply with redis-cluster.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromBlockingListCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
if (count($arguments) === 2) {
return $arguments[0];
}
}
/**
* Extracts the key from EVAL and EVALSHA commands.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromScriptingCommands(CommandInterface $command)
{
if ($command instanceof ScriptedCommand) {
$keys = $command->getKeys();
} else {
$keys = array_slice($args = $command->getArguments(), 2, $args[1]);
}
if (count($keys) === 1) {
return $keys[0];
}
}
/**
* {@inheritdoc}
*/
public function getHash(CommandInterface $command)
{
$hash = $command->getHash();
if (!isset($hash) && isset($this->commands[$cmdID = $command->getId()])) {
$key = call_user_func($this->commands[$cmdID], $command);
if (isset($key)) {
$hash = $this->hashGenerator->hash($key);
$command->setHash($hash);
}
}
return $hash;
}
/**
* {@inheritdoc}
*/
public function getKeyHash($key)
{
return $this->hashGenerator->hash($key);
}
}
+162
View File
@@ -0,0 +1,162 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Base class for Redis commands.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
abstract class AbstractCommand implements CommandInterface
{
private $hash;
private $arguments = array();
/**
* Returns a filtered array of the arguments.
*
* @param array $arguments List of arguments.
* @return array
*/
protected function filterArguments(Array $arguments)
{
return $arguments;
}
/**
* {@inheritdoc}
*/
public function setArguments(Array $arguments)
{
$this->arguments = $this->filterArguments($arguments);
unset($this->hash);
}
/**
* Sets the arguments array without filtering.
*
* @param array $arguments List of arguments.
*/
public function setRawArguments(Array $arguments)
{
$this->arguments = $arguments;
unset($this->hash);
}
/**
* {@inheritdoc}
*/
public function getArguments()
{
return $this->arguments;
}
/**
* Gets the argument from the arguments list at the specified index.
*
* @param array $arguments Position of the argument.
*/
public function getArgument($index)
{
if (isset($this->arguments[$index])) {
return $this->arguments[$index];
}
}
/**
* {@inheritdoc}
*/
public function setHash($hash)
{
$this->hash = $hash;
}
/**
* {@inheritdoc}
*/
public function getHash()
{
if (isset($this->hash)) {
return $this->hash;
}
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data;
}
/**
* Helper function used to reduce a list of arguments to a string.
*
* @param string $accumulator Temporary string.
* @param string $argument Current argument.
* @return string
*/
protected function toStringArgumentReducer($accumulator, $argument)
{
if (strlen($argument) > 32) {
$argument = substr($argument, 0, 32) . '[...]';
}
$accumulator .= " $argument";
return $accumulator;
}
/**
* Returns a partial string representation of the command with its arguments.
*
* @return string
*/
public function __toString()
{
return array_reduce(
$this->getArguments(),
array($this, 'toStringArgumentReducer'),
$this->getId()
);
}
/**
* Normalizes the arguments array passed to a Redis command.
*
* @param array $arguments Arguments for a command.
* @return array
*/
public static function normalizeArguments(Array $arguments)
{
if (count($arguments) === 1 && is_array($arguments[0])) {
return $arguments[0];
}
return $arguments;
}
/**
* Normalizes the arguments array passed to a variadic Redis command.
*
* @param array $arguments Arguments for a command.
* @return array
*/
public static function normalizeVariadic(Array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
return array_merge(array($arguments[0]), $arguments[1]);
}
return $arguments;
}
}
+76
View File
@@ -0,0 +1,76 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Defines an abstraction representing a Redis command.
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface CommandInterface
{
/**
* Gets the ID of a Redis command.
*
* @return string
*/
public function getId();
/**
* Set the hash for the command.
*
* @param int $hash Calculated hash.
*/
public function setHash($hash);
/**
* Returns the hash of the command.
*
* @return int
*/
public function getHash();
/**
* Sets the arguments for the command.
*
* @param array $arguments List of arguments.
*/
public function setArguments(Array $arguments);
/**
* Sets the raw arguments for the command without processing them.
*
* @param array $arguments List of arguments.
*/
public function setRawArguments(Array $arguments);
/**
* Gets the arguments of the command.
*
* @return array
*/
public function getArguments();
/**
* Gets the argument of the command at the specified index.
*
* @return array
*/
public function getArgument($index);
/**
* Parses a reply buffer and returns a PHP object.
*
* @param string $data Binary string containing the whole reply.
* @return mixed
*/
public function parseResponse($data);
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/auth
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionAuth extends AbstractCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'AUTH';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/echo
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionEcho extends AbstractCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'ECHO';
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/ping
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionPing extends AbstractCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PING';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data === 'PONG' ? true : false;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/quit
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionQuit extends AbstractCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'QUIT';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/select
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionSelect extends AbstractCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'SELECT';
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hdel
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashDelete extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HDEL';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(Array $arguments)
{
return self::normalizeVariadic($arguments);
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hexists
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashExists extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HEXISTS';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hget
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGet extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HGET';
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hgetall
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGetAll extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HGETALL';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
$result = array();
for ($i = 0; $i < count($data); $i++) {
$result[$data[$i]] = $data[++$i];
}
return $result;
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hmget
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGetMultiple extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HMGET';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(Array $arguments)
{
return self::normalizeVariadic($arguments);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hincrby
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashIncrementBy extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HINCRBY';
}
}
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hincrbyfloat
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashIncrementByFloat extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HINCRBYFLOAT';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hkeys
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashKeys extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HKEYS';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hlen
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashLength extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HLEN';
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hset
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSet extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HSET';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hmset
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSetMultiple extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HMSET';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(Array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$flattenedKVs = array($arguments[0]);
$args = $arguments[1];
foreach ($args as $k => $v) {
$flattenedKVs[] = $k;
$flattenedKVs[] = $v;
}
return $flattenedKVs;
}
return $arguments;
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hsetnx
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSetPreserve extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HSETNX';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hvals
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashValues extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HVALS';
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/del
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyDelete extends AbstractCommand implements PrefixableCommandInterface
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DEL';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(Array $arguments)
{
return self::normalizeArguments($arguments);
}
/**
* {@inheritdoc}
*/
public function prefixKeys($prefix)
{
PrefixHelpers::all($this, $prefix);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/dump
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyDump extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DUMP';
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/exists
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExists extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'EXISTS';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/expire
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExpire extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'EXPIRE';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/expireat
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExpireAt extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'EXPIREAT';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/keys
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyKeys extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'KEYS';
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/keys
* @author Daniele Alessandri <suppakilla@gmail.com>
* @deprecated
*/
class KeyKeysV12x extends KeyKeys
{
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return explode(' ', $data);
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/move
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyMove extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'MOVE';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/persist
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPersist extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PERSIST';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pexpire
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPreciseExpire extends KeyExpire
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PEXPIRE';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pexpireat
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPreciseExpireAt extends KeyExpireAt
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PEXPIREAT';
}
}
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pttl
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPreciseTimeToLive extends KeyTimeToLive
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PTTL';
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/randomkey
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRandom extends AbstractCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RANDOMKEY';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data !== '' ? $data : null;
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/rename
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRename extends AbstractCommand implements PrefixableCommandInterface
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RENAME';
}
/**
* {@inheritdoc}
*/
public function prefixKeys($prefix)
{
PrefixHelpers::all($this, $prefix);
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/renamenx
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRenamePreserve extends KeyRename
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RENAMENX';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/restore
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRestore extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RESTORE';
}
}
+117
View File
@@ -0,0 +1,117 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/sort
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeySort extends AbstractCommand implements PrefixableCommandInterface
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'SORT';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(Array $arguments)
{
if (count($arguments) === 1) {
return $arguments;
}
$query = array($arguments[0]);
$sortParams = array_change_key_case($arguments[1], CASE_UPPER);
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']) &&
count($sortParams['LIMIT']) == 2) {
$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'])) {
$query[] = 'STORE';
$query[] = $sortParams['STORE'];
}
return $query;
}
/**
* {@inheritdoc}
*/
public function prefixKeys($prefix)
{
if ($arguments = $this->getArguments()) {
$arguments[0] = "$prefix{$arguments[0]}";
if (($count = count($arguments)) > 1) {
for ($i = 1; $i < $count; $i++) {
switch ($arguments[$i]) {
case 'BY':
case 'STORE':
$arguments[$i] = "$prefix{$arguments[++$i]}";
break;
case 'GET':
$value = $arguments[++$i];
if ($value !== '#') {
$arguments[$i] = "$prefix$value";
}
break;
case 'LIMIT';
$i += 2;
break;
}
}
}
$this->setRawArguments($arguments);
}
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/ttl
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyTimeToLive extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'TTL';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/type
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyType extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'TYPE';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lindex
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListIndex extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LINDEX';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/linsert
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListInsert extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LINSERT';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/llen
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListLength extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LLEN';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lpop
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopFirst extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LPOP';
}
}
@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/blpop
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopFirstBlocking extends AbstractCommand implements PrefixableCommandInterface
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BLPOP';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(Array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[0])) {
list($arguments, $timeout) = $arguments;
array_push($arguments, $timeout);
}
return $arguments;
}
/**
* {@inheritdoc}
*/
public function prefixKeys($prefix)
{
PrefixHelpers::skipLast($this, $prefix);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/rpop
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLast extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RPOP';
}
}
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/brpop
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLastBlocking extends ListPopFirstBlocking
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BRPOP';
}
}
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/rpoplpush
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLastPushHead extends AbstractCommand implements PrefixableCommandInterface
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RPOPLPUSH';
}
/**
* {@inheritdoc}
*/
public function prefixKeys($prefix)
{
PrefixHelpers::all($this, $prefix);
}
}
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/brpoplpush
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLastPushHeadBlocking extends AbstractCommand implements PrefixableCommandInterface
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BRPOPLPUSH';
}
/**
* {@inheritdoc}
*/
public function prefixKeys($prefix)
{
PrefixHelpers::skipLast($this, $prefix);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lpush
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPushHead extends ListPushTail
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LPUSH';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lpushx
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPushHeadX extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LPUSHX';
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/rpush
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPushTail extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RPUSH';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(Array $arguments)
{
return self::normalizeVariadic($arguments);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/rpushx
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPushTailX extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RPUSHX';
}
}

Some files were not shown because too many files have changed in this diff Show More