Compare commits

...

305 Commits

Author SHA1 Message Date
Daniele Alessandri 7db5e650ef Bump version and... release! 2010-11-28 20:23:39 +01:00
Daniele Alessandri 975bd98933 Update CHANGELOG. 2010-11-28 19:22:26 +01:00
Daniele Alessandri 7d5cccaf7c Overridden methods for UNWATCH and DISCARD in Predis\MultiExecBlock must return $this to not break method chaining. 2010-11-28 19:19:48 +01:00
Daniele Alessandri 08b1761e10 Fix a bunch of broken tests. 2010-11-28 17:21:46 +01:00
Daniele Alessandri 24f49abc14 Update CHANGELOG. 2010-11-28 16:54:02 +01:00
Daniele Alessandri b530f9f613 New command: PERSIST (Redis v2.2-dev). 2010-11-28 16:49:20 +01:00
Daniele Alessandri d677c72ae3 New command: ZREVRANGEBYSCORE (Redis v2.2-dev). 2010-11-28 16:39:07 +01:00
Daniele Alessandri c3cf2acb51 Update CHANGELOG. 2010-09-25 17:54:52 +02:00
Daniele Alessandri 8bfae69bf0 Make WATCH accept an array of keys that must be watched. 2010-09-25 17:52:23 +02:00
Daniele Alessandri 92d6ad62ce Test suite: add test for WATCH in Predis\MultiExecBlock. 2010-09-25 17:08:30 +02:00
Daniele Alessandri fa4d654d38 Update CHANGELOG. 2010-09-25 17:04:29 +02:00
Daniele Alessandri 9ac4571f91 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 16:55:31 +02:00
Daniele Alessandri fea5e1c602 Test suite: add missing tests for Predis\MultiExecBlock. 2010-09-25 16:53:21 +02:00
Daniele Alessandri ec51b14fbb Fix an infinite recursion loop when calling Predis\Client::multiExec() (ISSUE #14). 2010-09-25 16:52:59 +02:00
Daniele Alessandri dc56526256 New commands: RPUSHX, LPUSHX (Redis v2.2-dev). 2010-09-25 16:38:16 +02:00
Daniele Alessandri 195c6eae99 New command: LINSERT (Redis v2.2-dev). 2010-09-25 16:34:18 +02:00
Daniele Alessandri 9ae47c865e New command: STRLEN (Redis v2.2-dev). 2010-09-25 16:31:35 +02:00
Daniele Alessandri d3ba25436c Slight micro-optimization. 2010-09-25 16:26:55 +02:00
Daniele Alessandri 580cf80436 Get the connection out of a cluster only once inside Predis\Client::getClientFor(). 2010-09-18 18:56:39 +02:00
Daniele Alessandri d6bea4390e Perform (p)unsubscribtion only if (p)subscribed to a channel when the destructor for Predis\PubSubContext is invoked. 2010-09-17 22:24:59 +02:00
Daniele Alessandri fd7f4a7b48 Make arguments handling more solid for the SORT command. 2010-09-17 22:11:37 +02:00
Daniele Alessandri 92f7e8bf9b Remove useless code duplication. 2010-09-17 21:56:31 +02:00
Daniele Alessandri 006b44e4bf Fix checks on the value for the STORE argument (ISSUE #13). 2010-09-17 11:36:47 +02:00
Daniele Alessandri bd961a767b Update CHANGELOG. It's release time! 2010-07-11 17:12:28 +02:00
Daniele Alessandri b3599a8e6b Update CHANGELOG. 2010-07-11 17:11:58 +02:00
Daniele Alessandri 0b46de424e Test suite: add a test for the LIMIT modifier of ZRANGEBYSCORE configured via associative array. 2010-07-11 17:02:33 +02:00
Daniele Alessandri 73bdfc2d72 Fix handling of the WEIGHT modifier for ZUNIONSTORE and ZINTERSTORE with more than two weights specified. 2010-07-11 16:35:03 +02:00
Daniele Alessandri 3077e76708 Update CHANGELOG. 2010-07-11 16:24:06 +02:00
Daniele Alessandri 208fdf6daf Update CHANGELOG. 2010-07-11 15:32:14 +02:00
Daniele Alessandri 7d7995ff8f Update CHANGELOG. 2010-07-07 20:07:15 +02:00
Daniele Alessandri 418197af75 Add tests for the new options handling for the ZRANGE commands family. 2010-07-07 19:59:16 +02:00
Daniele Alessandri 90cb9d437d Add support for the LIMIT modifier in ZRANGEBYSCORE. 2010-07-07 19:58:52 +02:00
Daniele Alessandri 4668bdca83 ZRANGE, ZREVRANGE and ZRANGEBYSCORE accepts an array instance for optional modifiers. 2010-07-07 19:58:41 +02:00
Daniele Alessandri 93a3c184ed Bump version number. 2010-07-02 22:31:39 +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 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 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
Daniele Alessandri 3c33c74b35 Update CHANGELOG. Time to release! 2010-05-24 19:35:35 +02:00
Daniele Alessandri 0e69438c0b Remove dots at the end of exception messages. 2010-05-23 09:08:55 +02:00
Daniele Alessandri 761e3a2527 Add an optional argument to MultiBulkResponseIterator::sync() to specify if the synchronization of an iterable multibulk response should be performed by consuming the rest of the reply or by closing the underlying connection. 2010-05-23 09:03:17 +02:00
Daniele Alessandri 316d3de10e Add static method Predis\RedisServerProfile::getDevelopment() to create a new instance of the 'dev' server profile. 2010-05-23 08:47:29 +02:00
Daniele Alessandri 5757318c9b Remove Predis\RedisServerProfile::compareWith() since it is useless and it is broken anyway. 2010-05-23 08:45:22 +02:00
Daniele Alessandri 340dca8f8d Update CHANGELOG. 2010-05-23 08:44:19 +02:00
Daniele Alessandri c781e91c2e Fix typos. 2010-05-22 16:10:17 +02:00
Daniele Alessandri d04a95395d Update CHANGELOG. 2010-05-22 15:38:33 +02:00
Daniele Alessandri dad3704488 Examples: bulk update. 2010-05-22 14:48:37 +02:00
Daniele Alessandri d585b11bf5 Examples: add CustomDistributionStrategy.php 2010-05-22 14:34:28 +02:00
Daniele Alessandri 9f21cb12a2 Rename Predis\Distribution\IDistributionAlgorithm to Predis\Distribution\IDistributionStrategy. 2010-05-22 14:32:29 +02:00
Daniele Alessandri f60208eb54 Examples: updated PubSubContext.php 2010-05-22 13:59:16 +02:00
Daniele Alessandri f53ffbf42b Update TODO. 2010-05-22 13:52:30 +02:00
Daniele Alessandri c2a6350ff2 Update CHANGELOG. 2010-05-22 13:45:51 +02:00
Daniele Alessandri 57fc3cf30e Add VERSION file. 2010-05-22 13:30:56 +02:00
Daniele Alessandri 2aecdf25e5 Update README. 2010-05-22 13:24:04 +02:00
Daniele Alessandri 8ad161dc34 Update CHANGELOG. Start preparing for the release of Predis 0.6.0. 2010-05-22 13:07:10 +02:00
Daniele Alessandri 69a0ed6d2f Suppress PHP notices in Predis\ConnectionCluster::getConnectionById() if the specified connection alias/index does not exist. 2010-05-22 12:27:14 +02:00
Daniele Alessandri 1f663f06be Implement Predis\Client::getClientFor() to initialize a new client from a single connection of a cluster by its alias. 2010-05-22 12:22:34 +02:00
Daniele Alessandri 5427bdcf22 Fast fail on exceptions different than Predis\ServerException or Predis\CommunicationException. 2010-05-22 11:56:45 +02:00
Daniele Alessandri 6c512784cb Merge branch 'pipeline_partial_failures'
Conflicts:
	lib/Predis.php
2010-05-22 11:34:57 +02:00
Daniele Alessandri e70b8ed38b Fix an old but harmless glitch in the serialization of inline commands with 0 arguments. 2010-05-21 21:10:34 +02:00
Daniele Alessandri 40d5b04278 Change the behavior of a test and add a reminder for a later in-depth examination of alternatives. 2010-05-21 21:02:09 +02:00
Daniele Alessandri 71dab9a249 Update CHANGELOG. 2010-05-21 20:45:14 +02:00
Daniele Alessandri f0d2551244 Fix a wrong namespace path in SafeExecutor::execute(). 2010-05-21 20:34:20 +02:00
Daniele Alessandri 4c8ecc02e5 Force disconnection in Predis\Pipeline\StandardExecutor when a Predis\ServerException is thrown to prevent protocol desynchronization between the client and the server. 2010-05-21 20:31:40 +02:00
Daniele Alessandri 84cb28e3ac Merge branch 'pubsub_experimental' 2010-05-18 12:25:10 +02:00
Daniele Alessandri 6e321a3530 Add the Predis\Client::pubSubContext() method to initialize a PubSub context from a client instance. 2010-05-18 12:23:53 +02:00
Daniele Alessandri ea7e9a37c6 Remove unused variable in the pubsub example. 2010-05-18 11:54:59 +02:00
Daniele Alessandri 658272fe4b Fix wrong namespace path for EmptyRingException. 2010-05-18 11:46:42 +02:00
Daniele Alessandri fa6400dc22 Rename the \Predis\Utilities namespace to \Predis\Shared. 2010-05-17 20:05:10 +02:00
Daniele Alessandri 376dfe3451 Move IDistributionAlgorithm, HashRing and KetamaPureRing to the new Predis\Distribution namespace. 2010-05-17 19:59:54 +02:00
Daniele Alessandri a26932b3b5 Throw a more appropriate InvalidArgumentException when a non-callable argument is passed to instances of Predis\CommandPipeline and Predis\MultiExecBlock. 2010-05-17 19:46:28 +02:00
Daniele Alessandri eab70fb134 Implement the new EmptyRingException exception class. 2010-05-17 19:42:08 +02:00
Daniele Alessandri 23b68d9415 Test suite: add test for the HASH type. 2010-05-17 19:19:53 +02:00
Daniele Alessandri 5def6bebe3 Rename Predis\Pipeline\IExecutor to Predis\Pipeline\IPipelineExecutor. 2010-05-17 18:38:38 +02:00
Daniele Alessandri 35f7ea2895 Test suite: minor code formatting changes. 2010-05-16 13:00:01 +02:00
Daniele Alessandri 37d4aa3d94 Test suite: test creation of the server profile for Redis 2.0. 2010-05-16 12:29:19 +02:00
Daniele Alessandri f1890c93a6 Test suite: PredisClientFeaturesTestSuite and same signature of PHPUnit_Framework_TestCase::onNotSuccessfulTest. 2010-05-16 12:23:50 +02:00
Daniele Alessandri d69a872359 Test suite: minor code formatting changes. 2010-05-16 12:20:20 +02:00
Daniele Alessandri c01d185448 Bugfix: using the right variable in Predis\Pipeline\SafeExecutor makes things work. 2010-05-15 13:52:05 +02:00
Daniele Alessandri e4aec82836 Garbage in the LICENSE file. Seriously, what was that? 2010-05-15 13:08:06 +02:00
Daniele Alessandri 82619b4dd0 Updated CHANGELOG. 2010-05-15 13:06:08 +02:00
Daniele Alessandri b3f340f830 Handling of partial failures and server errors in pipelines. 2010-05-15 12:30:33 +02:00
Daniele Alessandri f7bdd9d64b The default server profile for Predis\Client is now 2.0 (supports for Redis v2.0 out of the box). 2010-05-15 08:30:55 +02:00
Daniele Alessandri 4f0f2ca3eb ZUNION and ZINTER have been changed to ZUNIONSTORE and ZINTERSTORE for consistency (see Redis commit 5d373da). 2010-05-15 08:25:50 +02:00
Daniele Alessandri 72ef71d2df Merge branch 'test_library_features' 2010-05-13 18:53:21 +02:00
Daniele Alessandri 1a9454a4f2 Updated the tests to reflect the changes that has been made in the internal design of the library in the latest weeks (preparing to merge into master). 2010-05-13 18:52:51 +02:00
Daniele Alessandri f6b3912783 Updated TODO. 2010-05-13 16:11:40 +02:00
Daniele Alessandri a0f441f21b Added an example to show how to use the \Predis\PubSubContext class. 2010-05-13 16:10:18 +02:00
Daniele Alessandri 7312dbe7fc Implemented the new \Predis\PubSubContext class. 2010-05-13 16:07:42 +02:00
Daniele Alessandri 27ebb1ddb5 New commands: PSUBSCRIBE and PUNSUBSCRIBE (Redis v2.0-dev). 2010-05-13 11:21:59 +02:00
Daniele Alessandri 08afdfac1b Trying to increment a string value associated to an hash now raises an error. 2010-05-10 11:27:26 +02:00
Daniele Alessandri 0664bde7e9 Bugfix: replies to DEL commands must not be converted to a boolean response as DEL returns the number of keys removed. 2010-05-10 11:20:51 +02:00
Daniele Alessandri ddafa2a101 Updated TODO. 2010-04-27 21:55:07 +02:00
Daniele Alessandri 2787905024 Test suite: updated tests for ZUNION and ZINTER with the new options style. 2010-04-27 21:54:37 +02:00
Daniele Alessandri b623144881 Better support for options for ZUNION and ZINTER. 2010-04-27 21:49:03 +02:00
Daniele Alessandri c73918b991 Fix a couple of command aliases in the DEV profile. 2010-04-24 14:01:24 +02:00
Daniele Alessandri b4e4ca0f0b Updated TODO. 2010-04-24 13:59:11 +02:00
Daniele Alessandri db6b8a3fc8 Parse response to HGETALL as an array of key => value. 2010-04-24 13:56:00 +02:00
Daniele Alessandri 0d4426c052 Test suite: added HKEYS, HVALS, HGETALL. 2010-04-24 13:54:06 +02:00
Daniele Alessandri 08adcb478d Test suite: added HLEN. 2010-04-24 13:16:34 +02:00
Daniele Alessandri 513baa556c Process responses to HDEL as boolean values. 2010-04-24 13:09:18 +02:00
Daniele Alessandri ead414c530 Test suite: added HDEL. 2010-04-24 13:09:05 +02:00
Daniele Alessandri 290e51877c Test suite: added HINCRBY. 2010-04-24 13:03:14 +02:00
Daniele Alessandri de2d75e1d7 Process responses to HEXISTS as boolean values. 2010-04-24 12:47:09 +02:00
Daniele Alessandri 7e61ac6f30 Test suite: added HEXISTS. 2010-04-24 12:45:34 +02:00
Daniele Alessandri 4f25e568cf Test suite: minor code formatting fix. 2010-04-24 12:42:37 +02:00
Daniele Alessandri 83ffdc8bc8 Various fixes for HMSET and HMSET. 2010-04-24 12:00:46 +02:00
Daniele Alessandri 7f138dfcef Test suite: added HMSET and HMGET. 2010-04-24 11:59:30 +02:00
Daniele Alessandri 55404a2400 Test suite: added HSETNX. 2010-04-24 11:37:09 +02:00
Daniele Alessandri 69bce63453 Test suite: added HGET. 2010-04-24 11:33:48 +02:00
Daniele Alessandri d79321ac8c Process responses to HSET as boolean values. 2010-04-24 11:26:35 +02:00
Daniele Alessandri c227eedbb7 Test suite: added HSET. 2010-04-24 11:24:55 +02:00
Daniele Alessandri 887fea87e5 Test suite: added ZREMRANGEBYRANK. 2010-04-24 11:12:35 +02:00
Daniele Alessandri 4b2400b570 Test suite: added ZINTER. 2010-04-24 10:26:34 +02:00
Daniele Alessandri a1c8568ccf Test suite: added ZUNION. 2010-04-24 10:21:29 +02:00
Daniele Alessandri fa039fe500 Test suite: renamed ZRANK test method. 2010-04-24 09:47:10 +02:00
Daniele Alessandri 9c587b6f1d Test suite: added ZREVRANK. 2010-04-24 09:46:32 +02:00
Daniele Alessandri 2f6b24c098 Test suite: added SETEX. 2010-04-24 09:37:54 +02:00
Daniele Alessandri 23de93582e Code formatting. 2010-04-24 09:19:51 +02:00
Daniele Alessandri e8a443200b Updated CHANGELOG. 2010-04-23 21:38:43 +02:00
Daniele Alessandri 2eb68a0fee New command: SETEX (Redis v2.0-dev). 2010-04-23 21:38:22 +02:00
Daniele Alessandri 77c8b702b6 Updated CHANGELOG. 2010-04-23 21:33:30 +02:00
Daniele Alessandri a092ad46a3 New command: HSETNX (Redis v2.0-dev). 2010-04-23 21:32:10 +02:00
Daniele Alessandri b5e2b9eee9 New command: HMGET (Redis v2.0-dev). 2010-04-23 21:28:43 +02:00
Daniele Alessandri 7c2866f9fc Updating comments. 2010-04-23 21:23:23 +02:00
Daniele Alessandri 801c4647fd Slight code reorganization. 2010-04-23 21:22:46 +02:00
Daniele Alessandri 0e224ec492 Updated CHANGELOG. 2010-04-23 21:18:33 +02:00
Daniele Alessandri b93b13133d New command: HMSET (Redis v2.0-dev). 2010-04-23 21:12:46 +02:00
Daniele Alessandri d9542c21c3 New commands: PUBLISH, SUBSCRIBE and UNSUBSCRIBE (Redis v2.0-dev). 2010-04-23 21:07:36 +02:00
Daniele Alessandri 0a0afec0ec Reminder for ZUNION. 2010-04-23 20:54:41 +02:00
Daniele Alessandri 7ae94ca6fb New \Predis\Protocol class. It's not really used right now... 2010-04-23 20:37:57 +02:00
Daniele Alessandri df9fef8900 Moved the IDistributionAlgorithm interface one level up in the namespace chain, from \Predis\Utilities to \Predis. 2010-04-23 20:27:59 +02:00
Daniele Alessandri d369f8bb74 Renamed the \Predis\Utilities\IRing interface to \Predis\Utilities\IDistributionAlgorithm. 2010-04-21 22:13:13 +02:00
Daniele Alessandri 8ae5197a38 Moved the initialization of the default \Predis\ResponseReader instance. 2010-04-21 21:32:19 +02:00
Daniele Alessandri b662b36e95 Updated CHANGELOG. 2010-04-19 20:51:17 +02:00
Daniele Alessandri ce790071fd Providing optional support for Redis 1.0. 2010-04-19 16:52:49 +02:00
Daniele Alessandri f729793a9a Drop default support for Redis 1.0 and switch to the multi-bulk type for all of the Redis commands. 2010-04-19 16:50:50 +02:00
Daniele Alessandri ad2381a305 Users can now register their own \Predis\RedisServerProfile class with one or more aliases. 2010-04-19 15:37:43 +02:00
Daniele Alessandri 3121b1761e Changed the way arguments of SORT are prepared before serialization to be compatible with an eventual switch to multi-bulk requests. 2010-04-18 14:33:22 +02:00
Daniele Alessandri 81aef81b55 Changed the way arguments of SLAVEOF are prepared before serialization to be compatible with an eventual switch to multi-bulk requests. 2010-04-18 13:09:03 +02:00
Daniele Alessandri ea9c3388da No more implicit serialization for an array instance passed as the single argument of a MultiBulkCommand. 2010-04-18 12:49:38 +02:00
Daniele Alessandri 24bf10c691 Some performance improvements in the serialization of multibulk commands. 2010-04-18 10:53:38 +02:00
Daniele Alessandri ea75b168d1 Re-enabled the test for SORT in the test suite. 2010-04-17 20:12:39 +02:00
Daniele Alessandri 9fc0fbdb8c Converted the test suite to invoke commands on the client instance as they are defined in the command reference of Redis instead of using their aliases. 2010-04-17 19:50:24 +02:00
Daniele Alessandri a35faf2df6 Fixed a bug in the registration of the RPOPLPUSH command. 2010-04-17 18:48:13 +02:00
Daniele Alessandri 471102253a Updated the tests to reflect the changes that has been made in the internal design of the library in the latest weeks. 2010-04-17 17:04:06 +02:00
Daniele Alessandri 4cce99d388 Merge branch 'test_client_feats' into test_library_features 2010-04-17 17:02:36 +02:00
Daniele Alessandri 39052b181e Merge branch 'hyves_contribs_port' into master. Huge thanks to Lorenzo Castelli for a lot of awesome improvements and ideas. 2010-04-17 15:26:21 +02:00
Daniele Alessandri fd0732ca33 Updated CHANGELOG. 2010-04-17 15:14:03 +02:00
Daniele Alessandri 14c9d36bdf Updated CHANGELOG. 2010-04-17 15:13:48 +02:00
Daniele Alessandri 323b6f9eff Updated README. 2010-04-17 15:12:31 +02:00
Daniele Alessandri 835f5a5cbc Added Lorenzo to the contributors list. 2010-04-17 14:51:43 +02:00
Daniele Alessandri fa22d9667c Get a Predis\ResponseError instance from a Predis\ServerException instance. 2010-04-17 09:13:32 +02:00
Daniele Alessandri 67a27a7f05 Fixed a bug in the test suite discovered thanks to the validation of numeric inputs introduced in Redis with commit bbe025e. 2010-04-15 11:55:15 +02:00
Daniele Alessandri bdf4dfb54e Fixed the test suite to comply with the changes introduced in Redis with commit 4e27f26. 2010-04-15 11:51:33 +02:00
Daniele Alessandri a3ead444b2 Removed a spurious new line. 2010-04-14 15:52:35 +02:00
Daniele Alessandri 9facda2678 Updated CHANGELOG. 2010-04-14 15:51:08 +02:00
Daniele Alessandri 8f50c5f69a User-configurable key distribution algorithm to be used for clustered connections. 2010-04-14 15:03:27 +02:00
Daniele Alessandri 0e52825d97 Merge branch 'client_options' into hyves_contribs_port 2010-04-14 14:36:39 +02:00
Daniele Alessandri 5842c57b28 Merge branch 'hashring_rework' into hyves_contribs_port 2010-04-14 14:36:10 +02:00
Daniele Alessandri c89e222fd2 Removed the setOption and getOption methods from ResponseReader as their implementation was clumsy. 2010-04-10 19:10:20 +02:00
Daniele Alessandri c8509dd068 Exposed ResponseReader::setHandler as a public method and implemented ResponseReader::getHandler. 2010-04-10 18:48:42 +02:00
Daniele Alessandri 9e97c15a50 \Predis\Client now uses the new \Predis\ClientOptions class to define various options for a client instance. 2010-04-10 17:45:40 +02:00
Daniele Alessandri c4341eafaa Implemented the new \Predis\ClientOptions class. 2010-04-10 16:53:19 +02:00
Daniele Alessandri a2617af24a Removed a spurious space character. 2010-04-10 16:45:58 +02:00
Daniele Alessandri ea04aa67c6 Bugfix: missing backslash for root namespace. 2010-04-10 16:44:50 +02:00
Daniele Alessandri 994258b4c2 Slightly changed the arguments order of HashRing::addNodeToRing. 2010-04-10 11:01:03 +02:00
Daniele Alessandri 2272982364 Updated CHANGELOG. 2010-04-10 10:55:43 +02:00
Daniele Alessandri 3a44f766c2 Close the underlying connection also on MalformedServerResponse exceptions. 2010-04-10 10:15:05 +02:00
Daniele Alessandri bac4be648f Added support for persistent connections (optional and experimental). 2010-04-10 10:13:01 +02:00
Daniele Alessandri a1c827cb2d Precalculate the total count of the keys in the ring. 2010-04-10 09:25:58 +02:00
Daniele Alessandri 1ba033879a Bugfix: wrap-around differences for HashRing and KetamaPureRing. 2010-04-10 09:05:49 +02:00
Daniele Alessandri 79e7b0b209 Predis\ConnectionCluster now accepts an instance of Predis\Utilities\IRing as an optional parameter. 2010-04-04 23:32:21 +02:00
Daniele Alessandri 6d5060b226 Introduced the new KetamaPureRing class with the same key distribution algorithm of libketama. 2010-04-04 23:14:57 +02:00
Daniele Alessandri a6c246ac7c Small refactoring of the HashRing class. 2010-04-04 23:13:22 +02:00
Daniele Alessandri 18e181d9a1 Introduced the new IRing interface. 2010-04-04 20:13:07 +02:00
Daniele Alessandri aafddece15 Removed code duplication. 2010-04-03 14:01:53 +02:00
Daniele Alessandri 45ed2a9f96 Changed the parameters order for the constructor of the \Predis\CommunicationException class. 2010-04-03 13:31:49 +02:00
Daniele Alessandri 958c0a3b33 Disallow length parameter <= 0 for Connection::readBytes. 2010-04-03 13:23:42 +02:00
Daniele Alessandri f7f9e3483c Reworked the CommunicationException class and its derivates; When specified, close the underlying connection after a CommunicationException has been trown. 2010-04-02 18:53:42 +02:00
Daniele Alessandri 5040ce7d18 New exceptions hierarchy. 2010-04-02 16:58:19 +02:00
Daniele Alessandri 41f2065287 Removed a couple of redundant destructors. 2010-04-02 14:57:14 +02:00
Daniele Alessandri 617d5f69b6 Added support for async connections. 2010-04-02 14:28:15 +02:00
Daniele Alessandri c87fc91fb4 Bugfix: close the connection on Connection::rawCommand when the parameter is set to TRUE. 2010-04-02 14:15:10 +02:00
Daniele Alessandri 4cc43775b0 Added support for partial reads/writes. Note that stream_get_contents is not used because it doesn't seem to respect the timeout. 2010-04-02 13:51:46 +02:00
Daniele Alessandri 1b5e148d96 \Predis\Client::setProfile now accepts also a string value to specify the target server profile. 2010-03-31 12:23:10 +02:00
Daniele Alessandri 981b64fe21 Added support for weight parameter in server configuration, that allows to balance the keys asymmetrically across multiple servers. 2010-03-31 12:04:11 +02:00
Daniele Alessandri 6ea96dcfea Added support for optionally weighted nodes in the hashring. 2010-03-31 11:47:33 +02:00
Daniele Alessandri 435fdeab75 Add support for microsecond read/write timeouts. 2010-03-31 10:40:14 +02:00
Daniele Alessandri 4f3ffe1217 Made ConnectionCluster::getConnection public, so client code can check to which server a command would be sent. 2010-03-30 15:47:39 +02:00
Daniele Alessandri 486244be8d Reworked HashRing::remove to handle hash collisions in a consistent way. 2010-03-30 15:41:31 +02:00
Daniele Alessandri 2cd056cab6 Added wrapping around for the consistent hashing algorithm. 2010-03-30 15:03:49 +02:00
Daniele Alessandri 1c286f29d1 executeCommand is now part of the IConnection interface; Moved the actual implementation of executeCommand() from Predis\Client to Predis\Connection. 2010-03-30 12:53:11 +02:00
Daniele Alessandri 631832eaf9 Updated CHANGELOG. 2010-03-29 12:48:54 +02:00
Daniele Alessandri 17dff871bc New command: HINCRBY (Redis v2.0-dev). 2010-03-29 12:46:43 +02:00
Daniele Alessandri f3d7e5d31b New command: HEXISTS (Redis v2.0-dev). 2010-03-29 12:46:37 +02:00
Daniele Alessandri 2d8007448f New command: CONFIG (Redis v2.0-dev). 2010-03-29 12:46:30 +02:00
Daniele Alessandri de1b8f45bc Fixed failing tests due to changes introduced in Redis commits 3ea27d3, cc3b01c and 653c924. 2010-03-29 12:46:23 +02:00
Daniele Alessandri a0350b2726 Updated CHANGELOG. 2010-03-17 08:20:13 +01:00
Daniele Alessandri 70907adcce Wrong parameters passed to ClientException on write errors over the network. 2010-03-16 22:53:50 +01:00
Daniele Alessandri 8391326de6 New command: HLEN (Redis v2.0-dev). 2010-03-16 17:40:24 +01:00
Daniele Alessandri 9e417d9bcf New command: HGETALL (Redis v2.0-dev). 2010-03-16 17:24:58 +01:00
Daniele Alessandri 13ae4c4ca0 New command: HVALS (Redis v2.0-dev). 2010-03-16 17:22:46 +01:00
Daniele Alessandri cd8674c2f7 New command: HKEYS (Redis v2.0-dev). 2010-03-16 17:21:55 +01:00
Daniele Alessandri 5787b4b9fd New command: HDEL (Redis v2.0-dev). 2010-03-16 17:20:25 +01:00
Daniele Alessandri 0fed80279a Renamed \Predis\Commands\HSet and \Predis\Commands\HGet to \Predis\Commands\HashSet and \Predis\Commands\HashGet for naming consistency. 2010-03-16 17:18:25 +01:00
Daniele Alessandri 1fe49863b4 Do not parse ResponseError instances. 2010-03-11 19:08:47 +01:00
Daniele Alessandri ee9868c123 Instead of passing around the actual socket resource, pass the Connection instance as argument. 2010-03-11 11:23:35 +01:00
Daniele Alessandri ce00cb14b9 Bugfix: the response parser for KEYS raises a E_WARNING when using iterators for multi bulk responses against Redis v2.0. 2010-03-10 23:45:04 +01:00
Daniele Alessandri daf6dbd7c2 // TODO stuff. 2010-03-10 18:48:32 +01:00
Daniele Alessandri ff103b385d Tests for Predis\CommandPipeline. 2010-03-10 18:46:45 +01:00
Daniele Alessandri 4dc9affda2 Minor adjustments in a few tests. 2010-03-10 17:48:28 +01:00
Daniele Alessandri e9e2b8ca5a Tests for Predis\ResponseReader. 2010-03-10 17:46:35 +01:00
Daniele Alessandri 1394df5625 Tests for Predis\Connection. 2010-03-10 16:24:09 +01:00
Daniele Alessandri 5b039c8ee8 Tests for Predis\Command. 2010-03-10 14:59:34 +01:00
Daniele Alessandri 90078b95c7 Tests for Predis\ResponseError. 2010-03-10 14:18:28 +01:00
Daniele Alessandri 83ab54e9fc Tests for Predis\ResponseQueued. 2010-03-10 14:18:15 +01:00
Daniele Alessandri ea9f83d4c0 Tests for Predis\RedisServerProfile. 2010-03-10 14:04:43 +01:00
Daniele Alessandri 95664719a6 Tests for Predis\ConnectionParameters. 2010-03-10 12:04:17 +01:00
Daniele Alessandri f02b5494c7 New set of tests for client-specific features. 2010-03-10 11:47:41 +01:00
Daniele Alessandri c95125bae1 Allow a null parameters argument for the ConnectionParameters constructor (set to default). 2010-03-10 11:44:22 +01:00
Daniele Alessandri 53970bd006 Constants defined for Redis reply types in ResponseReader. 2010-03-10 11:27:28 +01:00
Daniele Alessandri 3b150d4d86 Get ResponseReader options values. 2010-03-10 11:18:25 +01:00
Daniele Alessandri 83c071b7ff Updated CHANGELOG. 2010-03-09 16:47:42 +01:00
Daniele Alessandri cadb7f8a3f Updated TODO. 2010-03-09 16:47:21 +01:00
Daniele Alessandri 52a1c06ac2 New command: ZINTER (Redis v2.0-dev). 2010-03-09 16:43:23 +01:00
Daniele Alessandri b21b6f8029 New command: ZUNION (Redis v2.0-dev). 2010-03-09 16:41:32 +01:00
Daniele Alessandri fa62b75b9a New command: ZREMRANGEBYRANK (Redis v2.0-dev). 2010-03-09 16:37:54 +01:00
Daniele Alessandri 52815adc0f New command: ZREVRANK (Redis v2.0-dev). 2010-03-09 16:33:03 +01:00
Daniele Alessandri 4e4c26c4e6 Updated CHANGELOG. 2010-03-09 16:17:32 +01:00
Daniele Alessandri 38e8c94f8b Enhancement: set how to handle errors returned by the server (throw a ServerException or return a ResponseError instance that holds the error message). 2010-03-09 16:01:33 +01:00
Daniele Alessandri c281891641 Enhancement: set how to handle multibulk replies (fetch the whole reply or stream it down to the user code). 2010-03-09 15:48:41 +01:00
Daniele Alessandri 425c9ade31 Fixed internal API divergences between the two merged branches. 2010-03-09 15:19:22 +01:00
Daniele Alessandri d03c7c42b2 Merge branch 'multibulk_streaming_new'
Conflicts:
	lib/Predis.php
2010-03-09 14:57:38 +01:00
Daniele Alessandri 9086cfc284 Merge branch 'response_reader' into integration 2010-03-09 14:53:53 +01:00
Daniele Alessandri d8b0c7f40c Reverting some obsoleted changes in preparation for a merge into the master branch. 2010-03-09 14:47:15 +01:00
Daniele Alessandri 51c7ef4566 Get the current ResponseReader associated to a Connection instance. 2010-03-09 14:07:41 +01:00
Daniele Alessandri 84ea64a9ee Enhanchement: added the ability to set options on a ResponseReader instance. 2010-03-09 14:06:10 +01:00
Daniele Alessandri e04ed827d4 Get the current ResponseReader associated to a Client instance. 2010-03-09 13:00:43 +01:00
Daniele Alessandri 5cae45282d Fixed tests against the current Redis master branch. 2010-03-09 12:54:18 +01:00
Daniele Alessandri 011d544d06 Fixed a small oversight. 2010-03-09 12:44:37 +01:00
Daniele Alessandri c6f97a7946 New commands: HSET and HGET (Redis v2.0-dev). NOTE: this is the beginning of the Hashes awesomeness in Redis! 2010-03-06 09:30:37 +01:00
Daniele Alessandri c06f9e34bf Added tests for DISCARD. 2010-03-04 14:18:38 +01:00
Daniele Alessandri ac8172235f Added tests for ZRANK. 2010-03-04 14:09:56 +01:00
Daniele Alessandri 5aaec9d11b Added a test for SUBSTR on integer-encoded values. 2010-03-04 13:58:05 +01:00
Daniele Alessandri 3e0d9c8400 Added tests for SUBSTR. 2010-03-04 12:41:51 +01:00
Daniele Alessandri 9ac0d26a48 Minor code rearrangements. 2010-03-04 12:29:23 +01:00
Daniele Alessandri 4645e7508e Updated CHANGELOG. 2010-03-04 12:21:06 +01:00
Daniele Alessandri 46d2344d42 Renamed \Predis\Commands\ZCount to \Predis\Commands\ZSetCount for naming consistency. 2010-03-04 12:17:09 +01:00
Daniele Alessandri 28bac3b3b0 New command: ZRANK (Redis v2.0-dev). 2010-03-04 12:16:14 +01:00
Daniele Alessandri 0b229a5057 New command: SUBSTR (Redis v2.0-dev). 2010-03-04 12:12:10 +01:00
Daniele Alessandri 460dfce7f6 Minor code rearrangements. 2010-03-04 11:58:19 +01:00
Daniele Alessandri 138a171f6c Updated CHANGELOG. 2010-03-02 17:21:38 +01:00
Daniele Alessandri 03ce35f5d9 Enhancement: added support for DISCARD in a MULTI / EXEC context initialized inside of an instance of MultiExecBlock. 2010-03-02 17:15:49 +01:00
Daniele Alessandri c325707559 New command: DISCARD (Redis v2.0-dev). 2010-03-02 16:49:15 +01:00
Daniele Alessandri e114f86d64 Predis\Commands\Keys has been adjusted to handle the change in its reply type in a backwards-compatible way (the KEYS command will return a multibulk reply starting from Redis 2.0). 2010-03-02 16:47:18 +01:00
Daniele Alessandri 8a79343f12 Private utility method ResponseReader->setHandler. 2010-03-01 18:52:34 +01:00
Daniele Alessandri f0d5dad2c1 Non-static response reader 2010-03-01 12:22:39 +01:00
Daniele Alessandri 6e5c495b26 MultiBulkResponseIterator and MultiBulkResponseKVIterator inherit from a common MultiBulkResponseIteratorBase class. 2010-02-27 12:44:36 +01:00
Daniele Alessandri f65cb529ec Enhancement: when using multibulk response iterators, ZRANGE and ZSETRANGE now return a MultiBulkResponseKVIterator when called with the WITHSCORES parameter. 2010-02-27 11:52:14 +01:00
Daniele Alessandri 47db5075ec Implemented MultiBulkResponseKVIterator to wrap iterators that should be seen as $k => array($MBIterator, $MBIterator++) by external code (think of ZRANGE .. WITHSCORES as a practical example). 2010-02-27 11:46:52 +01:00
Daniele Alessandri c8554f2c8b Added tests for APPEND. 2010-02-27 11:15:31 +01:00
Daniele Alessandri 4c6dedabe0 Added tests for ZCOUNT. 2010-02-27 11:08:05 +01:00
Daniele Alessandri 5ad2abd149 Added test for ZRANGEBYSCORE with the WITHSCORES parameter. 2010-02-27 10:59:34 +01:00
Daniele Alessandri 7f8ba42717 Enhancement: ZRANGEBYSCORE now handles the WITHSCORES (Redis v2.0-dev) just like ZRANGE and ZREVRANGE. 2010-02-27 10:54:42 +01:00
Daniele Alessandri ad540776ac New command: ZCOUNT (Redis v2.0-dev). 2010-02-27 10:39:47 +01:00
Daniele Alessandri 90d7b252ae New command: APPEND (Redis v2.0-dev). 2010-02-27 10:31:57 +01:00
Daniele Alessandri 90dc794780 Updated CHANGELOG. 2010-02-27 09:49:04 +01:00
Daniele Alessandri 21abe8bf02 Enhancement: connections can now be identified by an alias (useful when connected to a cluster of Redis servers). 2010-02-27 09:43:49 +01:00
Daniele Alessandri 806ef6058d Increased the default connection timeout from 2 to 5 seconds and moved the initialization of its value in ConnectionParameters->filterConnectionParams. 2010-02-26 23:22:13 +01:00
Daniele Alessandri e229153800 Use instanceof instead of the deprecated is_a(). 2010-02-26 23:04:42 +01:00
Daniele Alessandri 7579b2c6b7 Updated CHANGELOG. 2010-02-26 22:53:25 +01:00
Daniele Alessandri ee5e978a6c Enhancement: allows method chaining with multi-exec blocks. 2010-02-26 22:47:39 +01:00
Daniele Alessandri dac4d87a60 Bugfix: the pipeline command buffer is not correctly reinitialized after CommandPipeline->flushPipeline. 2010-02-26 22:42:09 +01:00
Daniele Alessandri 47d34531e3 Enhancement: allows method chaining with commands pipelines. 2010-02-26 22:30:57 +01:00
Daniele Alessandri 0cd7b446c6 Users can now choose at runtime how to handle multibulk responses. The new (and optional) iterator-based approach is slightly slower than the usual fetch-all behaviour, but in certain scenarios (e.g. when dealing with massive multibulk responses) it can save a lot of memory on the client. 2010-02-25 12:46:10 +01:00
Daniele Alessandri 8df4eaf7b3 Predis\Client::multiExec is now aware of iterable multi-bulk responses. 2010-02-25 12:44:13 +01:00
Daniele Alessandri 701e06443c Ensure that iterators returned by multi-bulk replies inside a pipeline are stored in the replies buffer as arrays. 2010-02-25 12:43:16 +01:00
Daniele Alessandri a1aaf73b78 MultiBulkResponseIterator now implements the Countable interface. 2010-02-25 12:41:57 +01:00
Daniele Alessandri b7203d97e7 MultiBulkResponseIterator::current must be pre-fetched. 2010-02-25 12:41:19 +01:00
Daniele Alessandri a4720dc721 Implemented the new Predis\Utilities\MultiBulkResponseIterator. 2010-02-25 12:40:15 +01:00
Daniele Alessandri 2b0bd88d8f Updated CHANGELOG 2010-02-25 11:39:52 +01:00
Daniele Alessandri 4ee965ab83 Switched from anonymous functions to classes extending the IResponseHandler interface to handle server responses. 2010-02-25 11:28:14 +01:00
Daniele Alessandri 40df90007d Miss typings, oh my... 2010-02-25 11:27:07 +01:00
Daniele Alessandri 2c7a99df55 Added missing test for SORT ... GET 2010-02-25 11:24:50 +01:00
Daniele Alessandri 6e3b1c835d Enhancement: support multiple GET parameters for the SORT command (see also my comment in ISSUE #1) 2010-02-25 10:57:41 +01:00
Daniele Alessandri f5f06d643f Fixed a dangerous oversight in the test suite (seriously, testing FLUSHALL was indeed stupid). 2010-01-27 23:56:26 +01:00
16 changed files with 4268 additions and 771 deletions
+173 -2
View File
@@ -1,4 +1,175 @@
v0.5.1
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 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
@@ -10,5 +181,5 @@ v0.5.1
* 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
v0.5.0 (2010-01-09)
* First versioned release of Predis
+1 -1
View File
@@ -1,5 +1,5 @@
Copyright (c) 2009-2010 Daniele Alessandri
qq
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
+12 -10
View File
@@ -3,9 +3,7 @@
## About ##
Predis is a flexible and feature-complete PHP client library for the Redis key-value
database.
Predis is currently a work-in-progress and it comes in two flavors:
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.
@@ -17,12 +15,13 @@ Please refer to the TODO file to see which issues are still pending and what is
to be implemented soon in Predis.
## Features ##
## Main features ##
- Client-side sharding (support for consistent hashing of keys)
- 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
- 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 ##
@@ -83,7 +82,7 @@ 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\InlineCommand {
class BrandNewRedisCommand extends \Predis\MultiBulkCommand {
public function getCommandId() { return 'NEWCMD'; }
}
@@ -107,7 +106,7 @@ 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.
variable set to E_ALL | E_STRICT.
## Dependencies ##
@@ -133,6 +132,9 @@ variable set to E_ALL.
[Daniele Alessandri](mailto:suppakilla@gmail.com)
## Contributors ##
[Lorenzo Castelli](http://github.com/lcastelli)
## License ##
+4 -3
View File
@@ -1,6 +1,7 @@
* Documentation! The README is obviously not enought to show how to use
Predis as it does not cover all of its features.
* The included test suite covers almost all the Redis server commands, but a
full battery of tests targeting specific functions of this library is still
missing.
* Add more tests targeting specific functions of the library.
* Missing tests for commands:
PUBLISH, SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE
+1
View File
@@ -0,0 +1 @@
0.6.2
+3 -3
View File
@@ -1,10 +1,10 @@
<?php
require_once 'SharedConfigurations.php';
// when you have a whole set of consecutive commands to send to
// When you have a whole set of consecutive commands to send to
// a redis server, you can use a pipeline to improve performances.
$redis = Predis\Client::create($configurations);
$redis = new Predis\Client($single_server);
$replies = $redis->pipeline(function($pipe) {
$pipe->ping();
@@ -35,4 +35,4 @@ Array
)
*/
?>
?>
+60
View File
@@ -0,0 +1,60 @@
<?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.
class NaiveDistributionStrategy
implements Predis\Distribution\IDistributionStrategy {
private $_nodes, $_nodesCount;
public function __constructor() {
$this->_nodes = array();
$this->_nodesCount = 0;
}
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) {
return $n !== $node;
});
$this->_nodesCount = count($this->_nodes);
}
public function get($key) {
$count = $this->_nodesCount;
if ($count === 0) {
throw new RuntimeException('No connections');
}
return $this->_nodes[$count > 1 ? abs(crc32($key) % $count) : 0];
}
public function generateKey($value) {
return crc32($value);
}
}
$options = array(
'key_distribution' => new NaiveDistributionStrategy(),
);
$redis = 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");
}
$server1 = $redis->getClientFor('first')->info();
$server2 = $redis->getClientFor('second')->info();
printf("Server '%s' has %d keys while server '%s' has %d keys.\n",
'first', $server1['db15']['keys'], 'second', $server2['db15']['keys']
);
?>
+2 -2
View File
@@ -11,7 +11,7 @@ $mkv = array(
'usr:0003' => 'Third user'
);
$redis = Predis\Client::create($configurations);
$redis = new Predis\Client($single_server);
$redis->mset($mkv);
$retval = $redis->mget(array_keys($mkv));
@@ -26,4 +26,4 @@ Array
[2] => Third user
)
*/
?>
?>
+52
View File
@@ -0,0 +1,52 @@
<?php
require_once '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));
// Initialize a new pubsub context
$pubsub = $redis->pubSubContext();
// Subscribe to your channels
$pubsub->subscribe('control_channel');
$pubsub->subscribe('notifications');
// 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
foreach ($pubsub as $message) {
switch ($message->kind) {
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 {
echo "Received an unrecognized command: {$message->payload}.\n";
}
}
else {
echo "Received the following message from {$message->channel}:\n",
" {$message->payload}\n\n";
}
break;
}
}
// 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();
print_r("Goodbye from Redis v{$info['redis_version']}!\n");
?>
+17 -2
View File
@@ -1,9 +1,24 @@
<?php
require_once '../lib/Predis.php';
$configurations = array(
$single_server = array(
'host' => '127.0.0.1',
'port' => 6379,
'database' => 15
);
?>
$multiple_servers = array(
array(
'host' => '127.0.0.1',
'port' => 6379,
'database' => 15,
'alias' => 'first',
),
array(
'host' => '127.0.0.1',
'port' => 6380,
'database' => 15,
'alias' => 'second',
),
);
?>
+2 -2
View File
@@ -3,7 +3,7 @@ require_once 'SharedConfigurations.php';
// simple set and get scenario
$redis = Predis\Client::create($configurations);
$redis = new Predis\Client($single_server);
$redis->set('library', 'predis');
$retval = $redis->get('library');
@@ -13,4 +13,4 @@ print_r($retval);
/* OUTPUT
predis
*/
?>
?>
+1842 -416
View File
File diff suppressed because it is too large Load Diff
+500
View File
@@ -0,0 +1,500 @@
<?php
namespace Predis;
RedisServerProfile::registerProfile('\Predis\RedisServer_v1_0', '1.0');
class RedisServer_v1_0 extends \Predis\RedisServerProfile {
public function getVersion() { return '1.0'; }
public function getSupportedCommands() {
return array(
/* miscellaneous commands */
'ping' => '\Predis\Compatibility\v1_0\Commands\Ping',
'echo' => '\Predis\Compatibility\v1_0\Commands\DoEcho',
'auth' => '\Predis\Compatibility\v1_0\Commands\Auth',
/* connection handling */
'quit' => '\Predis\Compatibility\v1_0\Commands\Quit',
/* commands operating on string values */
'set' => '\Predis\Compatibility\v1_0\Commands\Set',
'setnx' => '\Predis\Compatibility\v1_0\Commands\SetPreserve',
'setPreserve' => '\Predis\Compatibility\v1_0\Commands\SetPreserve',
'get' => '\Predis\Compatibility\v1_0\Commands\Get',
'mget' => '\Predis\Compatibility\v1_0\Commands\GetMultiple',
'getMultiple' => '\Predis\Compatibility\v1_0\Commands\GetMultiple',
'getset' => '\Predis\Compatibility\v1_0\Commands\GetSet',
'getSet' => '\Predis\Compatibility\v1_0\Commands\GetSet',
'incr' => '\Predis\Compatibility\v1_0\Commands\Increment',
'increment' => '\Predis\Compatibility\v1_0\Commands\Increment',
'incrby' => '\Predis\Compatibility\v1_0\Commands\IncrementBy',
'incrementBy' => '\Predis\Compatibility\v1_0\Commands\IncrementBy',
'decr' => '\Predis\Compatibility\v1_0\Commands\Decrement',
'decrement' => '\Predis\Compatibility\v1_0\Commands\Decrement',
'decrby' => '\Predis\Compatibility\v1_0\Commands\DecrementBy',
'decrementBy' => '\Predis\Compatibility\v1_0\Commands\DecrementBy',
'exists' => '\Predis\Compatibility\v1_0\Commands\Exists',
'del' => '\Predis\Compatibility\v1_0\Commands\Delete',
'delete' => '\Predis\Compatibility\v1_0\Commands\Delete',
'type' => '\Predis\Compatibility\v1_0\Commands\Type',
/* commands operating on the key space */
'keys' => '\Predis\Compatibility\v1_0\Commands\Keys',
'randomkey' => '\Predis\Compatibility\v1_0\Commands\RandomKey',
'randomKey' => '\Predis\Compatibility\v1_0\Commands\RandomKey',
'rename' => '\Predis\Compatibility\v1_0\Commands\Rename',
'renamenx' => '\Predis\Compatibility\v1_0\Commands\RenamePreserve',
'renamePreserve' => '\Predis\Compatibility\v1_0\Commands\RenamePreserve',
'expire' => '\Predis\Compatibility\v1_0\Commands\Expire',
'expireat' => '\Predis\Compatibility\v1_0\Commands\ExpireAt',
'expireAt' => '\Predis\Compatibility\v1_0\Commands\ExpireAt',
'dbsize' => '\Predis\Compatibility\v1_0\Commands\DatabaseSize',
'databaseSize' => '\Predis\Compatibility\v1_0\Commands\DatabaseSize',
'ttl' => '\Predis\Compatibility\v1_0\Commands\TimeToLive',
'timeToLive' => '\Predis\Compatibility\v1_0\Commands\TimeToLive',
/* commands operating on lists */
'rpush' => '\Predis\Compatibility\v1_0\Commands\ListPushTail',
'pushTail' => '\Predis\Compatibility\v1_0\Commands\ListPushTail',
'lpush' => '\Predis\Compatibility\v1_0\Commands\ListPushHead',
'pushHead' => '\Predis\Compatibility\v1_0\Commands\ListPushHead',
'llen' => '\Predis\Compatibility\v1_0\Commands\ListLength',
'listLength' => '\Predis\Compatibility\v1_0\Commands\ListLength',
'lrange' => '\Predis\Compatibility\v1_0\Commands\ListRange',
'listRange' => '\Predis\Compatibility\v1_0\Commands\ListRange',
'ltrim' => '\Predis\Compatibility\v1_0\Commands\ListTrim',
'listTrim' => '\Predis\Compatibility\v1_0\Commands\ListTrim',
'lindex' => '\Predis\Compatibility\v1_0\Commands\ListIndex',
'listIndex' => '\Predis\Compatibility\v1_0\Commands\ListIndex',
'lset' => '\Predis\Compatibility\v1_0\Commands\ListSet',
'listSet' => '\Predis\Compatibility\v1_0\Commands\ListSet',
'lrem' => '\Predis\Compatibility\v1_0\Commands\ListRemove',
'listRemove' => '\Predis\Compatibility\v1_0\Commands\ListRemove',
'lpop' => '\Predis\Compatibility\v1_0\Commands\ListPopFirst',
'popFirst' => '\Predis\Compatibility\v1_0\Commands\ListPopFirst',
'rpop' => '\Predis\Compatibility\v1_0\Commands\ListPopLast',
'popLast' => '\Predis\Compatibility\v1_0\Commands\ListPopLast',
/* commands operating on sets */
'sadd' => '\Predis\Compatibility\v1_0\Commands\SetAdd',
'setAdd' => '\Predis\Compatibility\v1_0\Commands\SetAdd',
'srem' => '\Predis\Compatibility\v1_0\Commands\SetRemove',
'setRemove' => '\Predis\Compatibility\v1_0\Commands\SetRemove',
'spop' => '\Predis\Compatibility\v1_0\Commands\SetPop',
'setPop' => '\Predis\Compatibility\v1_0\Commands\SetPop',
'smove' => '\Predis\Compatibility\v1_0\Commands\SetMove',
'setMove' => '\Predis\Compatibility\v1_0\Commands\SetMove',
'scard' => '\Predis\Compatibility\v1_0\Commands\SetCardinality',
'setCardinality' => '\Predis\Compatibility\v1_0\Commands\SetCardinality',
'sismember' => '\Predis\Compatibility\v1_0\Commands\SetIsMember',
'setIsMember' => '\Predis\Compatibility\v1_0\Commands\SetIsMember',
'sinter' => '\Predis\Compatibility\v1_0\Commands\SetIntersection',
'setIntersection' => '\Predis\Compatibility\v1_0\Commands\SetIntersection',
'sinterstore' => '\Predis\Compatibility\v1_0\Commands\SetIntersectionStore',
'setIntersectionStore' => '\Predis\Compatibility\v1_0\Commands\SetIntersectionStore',
'sunion' => '\Predis\Compatibility\v1_0\Commands\SetUnion',
'setUnion' => '\Predis\Compatibility\v1_0\Commands\SetUnion',
'sunionstore' => '\Predis\Compatibility\v1_0\Commands\SetUnionStore',
'setUnionStore' => '\Predis\Compatibility\v1_0\Commands\SetUnionStore',
'sdiff' => '\Predis\Compatibility\v1_0\Commands\SetDifference',
'setDifference' => '\Predis\Compatibility\v1_0\Commands\SetDifference',
'sdiffstore' => '\Predis\Compatibility\v1_0\Commands\SetDifferenceStore',
'setDifferenceStore' => '\Predis\Compatibility\v1_0\Commands\SetDifferenceStore',
'smembers' => '\Predis\Compatibility\v1_0\Commands\SetMembers',
'setMembers' => '\Predis\Compatibility\v1_0\Commands\SetMembers',
'srandmember' => '\Predis\Compatibility\v1_0\Commands\SetRandomMember',
'setRandomMember' => '\Predis\Compatibility\v1_0\Commands\SetRandomMember',
/* multiple databases handling commands */
'select' => '\Predis\Compatibility\v1_0\Commands\SelectDatabase',
'selectDatabase' => '\Predis\Compatibility\v1_0\Commands\SelectDatabase',
'move' => '\Predis\Compatibility\v1_0\Commands\MoveKey',
'moveKey' => '\Predis\Compatibility\v1_0\Commands\MoveKey',
'flushdb' => '\Predis\Compatibility\v1_0\Commands\FlushDatabase',
'flushDatabase' => '\Predis\Compatibility\v1_0\Commands\FlushDatabase',
'flushall' => '\Predis\Compatibility\v1_0\Commands\FlushAll',
'flushDatabases' => '\Predis\Compatibility\v1_0\Commands\FlushAll',
/* sorting */
'sort' => '\Predis\Compatibility\v1_0\Commands\Sort',
/* remote server control commands */
'info' => '\Predis\Compatibility\v1_0\Commands\Info',
'slaveof' => '\Predis\Compatibility\v1_0\Commands\SlaveOf',
'slaveOf' => '\Predis\Compatibility\v1_0\Commands\SlaveOf',
/* persistence control commands */
'save' => '\Predis\Compatibility\v1_0\Commands\Save',
'bgsave' => '\Predis\Compatibility\v1_0\Commands\BackgroundSave',
'backgroundSave' => '\Predis\Compatibility\v1_0\Commands\BackgroundSave',
'lastsave' => '\Predis\Compatibility\v1_0\Commands\LastSave',
'lastSave' => '\Predis\Compatibility\v1_0\Commands\LastSave',
'shutdown' => '\Predis\Compatibility\v1_0\Commands\Shutdown',
);
}
}
/* ------------------------------------------------------------------------- */
namespace Predis\Compatibility\v1_0\Commands;
/* miscellaneous commands */
class Ping extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'PING'; }
public function parseResponse($data) {
return $data === 'PONG' ? true : false;
}
}
class DoEcho extends \Predis\BulkCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'ECHO'; }
}
class Auth extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'AUTH'; }
}
/* connection handling */
class Quit extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'QUIT'; }
public function closesConnection() { return true; }
}
/* commands operating on string values */
class Set extends \Predis\BulkCommand {
public function getCommandId() { return 'SET'; }
}
class SetPreserve extends \Predis\BulkCommand {
public function getCommandId() { return 'SETNX'; }
public function parseResponse($data) { return (bool) $data; }
}
class Get extends \Predis\InlineCommand {
public function getCommandId() { return 'GET'; }
}
class GetMultiple extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'MGET'; }
}
class GetSet extends \Predis\BulkCommand {
public function getCommandId() { return 'GETSET'; }
}
class Increment extends \Predis\InlineCommand {
public function getCommandId() { return 'INCR'; }
}
class IncrementBy extends \Predis\InlineCommand {
public function getCommandId() { return 'INCRBY'; }
}
class Decrement extends \Predis\InlineCommand {
public function getCommandId() { return 'DECR'; }
}
class DecrementBy extends \Predis\InlineCommand {
public function getCommandId() { return 'DECRBY'; }
}
class Exists extends \Predis\InlineCommand {
public function getCommandId() { return 'EXISTS'; }
public function parseResponse($data) { return (bool) $data; }
}
class Delete extends \Predis\InlineCommand {
public function getCommandId() { return 'DEL'; }
}
class Type extends \Predis\InlineCommand {
public function getCommandId() { return 'TYPE'; }
}
/* commands operating on the key space */
class Keys extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'KEYS'; }
public function parseResponse($data) {
return strlen($data) > 0 ? explode(' ', $data) : array();
}
}
class RandomKey extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'RANDOMKEY'; }
public function parseResponse($data) { return $data !== '' ? $data : null; }
}
class Rename extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'RENAME'; }
}
class RenamePreserve extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'RENAMENX'; }
public function parseResponse($data) { return (bool) $data; }
}
class Expire extends \Predis\InlineCommand {
public function getCommandId() { return 'EXPIRE'; }
public function parseResponse($data) { return (bool) $data; }
}
class ExpireAt extends \Predis\InlineCommand {
public function getCommandId() { return 'EXPIREAT'; }
public function parseResponse($data) { return (bool) $data; }
}
class DatabaseSize extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'DBSIZE'; }
}
class TimeToLive extends \Predis\InlineCommand {
public function getCommandId() { return 'TTL'; }
}
/* commands operating on lists */
class ListPushTail extends \Predis\BulkCommand {
public function getCommandId() { return 'RPUSH'; }
}
class ListPushHead extends \Predis\BulkCommand {
public function getCommandId() { return 'LPUSH'; }
}
class ListLength extends \Predis\InlineCommand {
public function getCommandId() { return 'LLEN'; }
}
class ListRange extends \Predis\InlineCommand {
public function getCommandId() { return 'LRANGE'; }
}
class ListTrim extends \Predis\InlineCommand {
public function getCommandId() { return 'LTRIM'; }
}
class ListIndex extends \Predis\InlineCommand {
public function getCommandId() { return 'LINDEX'; }
}
class ListSet extends \Predis\BulkCommand {
public function getCommandId() { return 'LSET'; }
}
class ListRemove extends \Predis\BulkCommand {
public function getCommandId() { return 'LREM'; }
}
class ListPopFirst extends \Predis\InlineCommand {
public function getCommandId() { return 'LPOP'; }
}
class ListPopLast extends \Predis\InlineCommand {
public function getCommandId() { return 'RPOP'; }
}
/* commands operating on sets */
class SetAdd extends \Predis\BulkCommand {
public function getCommandId() { return 'SADD'; }
public function parseResponse($data) { return (bool) $data; }
}
class SetRemove extends \Predis\BulkCommand {
public function getCommandId() { return 'SREM'; }
public function parseResponse($data) { return (bool) $data; }
}
class SetPop extends \Predis\InlineCommand {
public function getCommandId() { return 'SPOP'; }
}
class SetMove extends \Predis\BulkCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SMOVE'; }
public function parseResponse($data) { return (bool) $data; }
}
class SetCardinality extends \Predis\InlineCommand {
public function getCommandId() { return 'SCARD'; }
}
class SetIsMember extends \Predis\BulkCommand {
public function getCommandId() { return 'SISMEMBER'; }
public function parseResponse($data) { return (bool) $data; }
}
class SetIntersection extends \Predis\InlineCommand {
public function getCommandId() { return 'SINTER'; }
}
class SetIntersectionStore extends \Predis\InlineCommand {
public function getCommandId() { return 'SINTERSTORE'; }
}
class SetUnion extends \Predis\InlineCommand {
public function getCommandId() { return 'SUNION'; }
}
class SetUnionStore extends \Predis\InlineCommand {
public function getCommandId() { return 'SUNIONSTORE'; }
}
class SetDifference extends \Predis\InlineCommand {
public function getCommandId() { return 'SDIFF'; }
}
class SetDifferenceStore extends \Predis\InlineCommand {
public function getCommandId() { return 'SDIFFSTORE'; }
}
class SetMembers extends \Predis\InlineCommand {
public function getCommandId() { return 'SMEMBERS'; }
}
class SetRandomMember extends \Predis\InlineCommand {
public function getCommandId() { return 'SRANDMEMBER'; }
}
/* multiple databases handling commands */
class SelectDatabase extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SELECT'; }
}
class MoveKey extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'MOVE'; }
public function parseResponse($data) { return (bool) $data; }
}
class FlushDatabase extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'FLUSHDB'; }
}
class FlushAll extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'FLUSHALL'; }
}
/* sorting */
class Sort extends \Predis\InlineCommand {
public function getCommandId() { return 'SORT'; }
public function filterArguments(Array $arguments) {
if (count($arguments) === 1) {
return $arguments;
}
// TODO: add more parameters checks
$query = array($arguments[0]);
$sortParams = $arguments[1];
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'])) {
$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']) && $sortParams['store'] == true) {
$query[] = 'STORE';
$query[] = $sortParams['store'];
}
return $query;
}
}
/* persistence control commands */
class Save extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SAVE'; }
}
class BackgroundSave extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'BGSAVE'; }
public function parseResponse($data) {
if ($data == 'Background saving started') {
return true;
}
return $data;
}
}
class LastSave extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'LASTSAVE'; }
}
class Shutdown extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SHUTDOWN'; }
public function closesConnection() { return true; }
}
/* remote server control commands */
class Info extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'INFO'; }
public function parseResponse($data) {
$info = array();
$infoLines = explode("\r\n", $data, -1);
foreach ($infoLines as $row) {
list($k, $v) = explode(':', $row);
if (!preg_match('/^db\d+$/', $k)) {
$info[$k] = $v;
}
else {
$db = array();
foreach (explode(',', $v) as $dbvar) {
list($dbvk, $dbvv) = explode('=', $dbvar);
$db[trim($dbvk)] = $dbvv;
}
$info[$k] = $db;
}
}
return $info;
}
}
class SlaveOf extends \Predis\InlineCommand {
public function canBeHashed() { return false; }
public function getCommandId() { return 'SLAVEOF'; }
public function filterArguments(Array $arguments) {
if (count($arguments) === 0 || $arguments[0] === 'NO ONE') {
return array('NO', 'ONE');
}
return $arguments;
}
}
?>
+613
View File
@@ -0,0 +1,613 @@
<?php
define('I_AM_AWARE_OF_THE_DESTRUCTIVE_POWER_OF_THIS_TEST_SUITE', false);
require_once 'PHPUnit/Framework.php';
require_once 'PredisShared.php';
require_once '../lib/Predis_Compatibility.php';
class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
public $redis;
protected function setUp() {
$this->redis = RC::getConnection();
$this->redis->flushDatabase();
}
protected function tearDown() {
}
protected function onNotSuccessfulTest(Exception $exception) {
// drops and reconnect to a redis server on uncaught exceptions
RC::resetConnection();
parent::onNotSuccessfulTest($exception);
}
/* ConnectionParameters */
function testConnectionParametersDefaultValues() {
$params = new \Predis\ConnectionParameters();
$this->assertEquals(\Predis\ConnectionParameters::DEFAULT_HOST, $params->host);
$this->assertEquals(\Predis\ConnectionParameters::DEFAULT_PORT, $params->port);
$this->assertEquals(\Predis\ConnectionParameters::DEFAULT_TIMEOUT, $params->connection_timeout);
$this->assertNull($params->read_write_timeout);
$this->assertNull($params->database);
$this->assertNull($params->password);
$this->assertNull($params->alias);
}
function testConnectionParametersSetupValuesArray() {
$paramsArray = RC::getConnectionParametersArgumentsArray();
$params = new \Predis\ConnectionParameters($paramsArray);
$this->assertEquals($paramsArray['host'], $params->host);
$this->assertEquals($paramsArray['port'], $params->port);
$this->assertEquals($paramsArray['connection_timeout'], $params->connection_timeout);
$this->assertEquals($paramsArray['read_write_timeout'], $params->read_write_timeout);
$this->assertEquals($paramsArray['database'], $params->database);
$this->assertEquals($paramsArray['password'], $params->password);
$this->assertEquals($paramsArray['alias'], $params->alias);
}
function testConnectionParametersSetupValuesString() {
$paramsArray = RC::getConnectionParametersArgumentsArray();
$paramsString = RC::getConnectionParametersArgumentsString($paramsArray);
$params = new \Predis\ConnectionParameters($paramsArray);
$this->assertEquals($paramsArray['host'], $params->host);
$this->assertEquals($paramsArray['port'], $params->port);
$this->assertEquals($paramsArray['connection_timeout'], $params->connection_timeout);
$this->assertEquals($paramsArray['read_write_timeout'], $params->read_write_timeout);
$this->assertEquals($paramsArray['database'], $params->database);
$this->assertEquals($paramsArray['password'], $params->password);
$this->assertEquals($paramsArray['alias'], $params->alias);
}
/* Command and derivates */
function testCommand_TestArguments() {
$cmdArgs = array('key1', 'key2', 'key3');
$cmd = new \Predis\Commands\GetMultiple();
$cmd->setArgumentsArray($cmdArgs);
$this->assertEquals($cmdArgs[0], $cmd->getArgument(0));
$this->assertEquals($cmdArgs[1], $cmd->getArgument(1));
$this->assertEquals($cmdArgs[2], $cmd->getArgument(2));
$cmd = new \Predis\Commands\GetMultiple();
$cmd->setArguments('key1', 'key2', 'key3');
$this->assertEquals($cmdArgs[0], $cmd->getArgument(0));
$this->assertEquals($cmdArgs[1], $cmd->getArgument(1));
$this->assertEquals($cmdArgs[2], $cmd->getArgument(2));
$cmd = new \Predis\Commands\Ping();
$this->assertNull($cmd->getArgument(0));
}
function testCommand_InlineWithNoArguments() {
$cmd = new \Predis\Compatibility\v1_0\Commands\Ping();
$this->assertType('\Predis\InlineCommand', $cmd);
$this->assertEquals('PING', $cmd->getCommandId());
$this->assertFalse($cmd->closesConnection());
$this->assertFalse($cmd->canBeHashed());
$this->assertNull($cmd->getHash(new \Predis\Distribution\HashRing()));
$this->assertEquals("PING\r\n", $cmd());
}
function testCommand_InlineWithArguments() {
$cmd = new \Predis\Compatibility\v1_0\Commands\Get();
$cmd->setArgumentsArray(array('key'));
$this->assertType('\Predis\InlineCommand', $cmd);
$this->assertEquals('GET', $cmd->getCommandId());
$this->assertFalse($cmd->closesConnection());
$this->assertTrue($cmd->canBeHashed());
$this->assertNotNull($cmd->getHash(new \Predis\Distribution\HashRing()));
$this->assertEquals("GET key\r\n", $cmd());
}
function testCommand_BulkWithArguments() {
$cmd = new \Predis\Compatibility\v1_0\Commands\Set();
$cmd->setArgumentsArray(array('key', 'value'));
$this->assertType('\Predis\BulkCommand', $cmd);
$this->assertEquals('SET', $cmd->getCommandId());
$this->assertFalse($cmd->closesConnection());
$this->assertTrue($cmd->canBeHashed());
$this->assertNotNull($cmd->getHash(new \Predis\Distribution\HashRing()));
$this->assertEquals("SET key 5\r\nvalue\r\n", $cmd());
}
function testCommand_MultiBulkWithArguments() {
$cmd = new \Predis\Commands\SetMultiple();
$cmd->setArgumentsArray(array('key1', 'value1', 'key2', 'value2'));
$this->assertType('\Predis\MultiBulkCommand', $cmd);
$this->assertEquals('MSET', $cmd->getCommandId());
$this->assertFalse($cmd->closesConnection());
$this->assertFalse($cmd->canBeHashed());
$this->assertNotNull($cmd->getHash(new \Predis\Distribution\HashRing()));
$this->assertEquals("*5\r\n$4\r\nMSET\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n", $cmd());
}
function testCommand_ParseResponse() {
// default parser
$cmd = new \Predis\Commands\Get();
$this->assertEquals('test', $cmd->parseResponse('test'));
// overridden parser (boolean)
$cmd = new \Predis\Commands\Exists();
$this->assertTrue($cmd->parseResponse('1'));
$this->assertFalse($cmd->parseResponse('0'));
// overridden parser (boolean)
$cmd = new \Predis\Commands\Ping();
$this->assertTrue($cmd->parseResponse('PONG'));
// overridden parser (complex)
// TODO: emulate a respons to INFO
}
/* RedisServerProfile and derivates */
function testRedisServerProfile_GetSpecificVersions() {
$this->assertType('\Predis\RedisServer_v1_0', \Predis\RedisServerProfile::get('1.0'));
$this->assertType('\Predis\RedisServer_v1_2', \Predis\RedisServerProfile::get('1.2'));
$this->assertType('\Predis\RedisServer_v2_0', \Predis\RedisServerProfile::get('2.0'));
$this->assertType('\Predis\RedisServer_vNext', \Predis\RedisServerProfile::get('dev'));
$this->assertType('\Predis\RedisServerProfile', \Predis\RedisServerProfile::get('default'));
$this->assertEquals(\Predis\RedisServerProfile::get('default'), \Predis\RedisServerProfile::getDefault());
}
function testRedisServerProfile_SupportedCommands() {
$profile_10 = \Predis\RedisServerProfile::get('1.0');
$profile_12 = \Predis\RedisServerProfile::get('1.2');
$this->assertTrue($profile_10->supportsCommand('info'));
$this->assertTrue($profile_12->supportsCommand('info'));
$this->assertFalse($profile_10->supportsCommand('mset'));
$this->assertTrue($profile_12->supportsCommand('mset'));
$this->assertFalse($profile_10->supportsCommand('multi'));
$this->assertFalse($profile_12->supportsCommand('multi'));
}
function testRedisServerProfile_CommandsCreation() {
$profile = \Predis\RedisServerProfile::get('1.0');
$cmdNoArgs = $profile->createCommand('info');
$this->assertType('\Predis\Compatibility\v1_0\Commands\Info', $cmdNoArgs);
$this->assertNull($cmdNoArgs->getArgument());
$args = array('key1', 'key2');
$cmdWithArgs = $profile->createCommand('mget', $args);
$this->assertType('\Predis\Compatibility\v1_0\Commands\GetMultiple', $cmdWithArgs);
$this->assertEquals($args[0], $cmdWithArgs->getArgument()); // TODO: why?
$this->assertEquals($args[0], $cmdWithArgs->getArgument(0));
$this->assertEquals($args[1], $cmdWithArgs->getArgument(1));
$bogusCommand = 'not_existing_command';
$expectedMessage = "'$bogusCommand' is not a registered Redis command";
RC::testForClientException($this, $expectedMessage, function()
use($profile, $bogusCommand) {
$profile->createCommand($bogusCommand);
});
}
function testRedisServerProfile_CommandsRegistration() {
$profile = \Predis\RedisServerProfile::get('1.0');
$cmdId = 'mset';
$cmdClass = '\Predis\Commands\SetMultiple';
$this->assertFalse($profile->supportsCommand($cmdId));
$profile->registerCommand(new $cmdClass(), $cmdId);
$this->assertTrue($profile->supportsCommand($cmdId));
$this->assertType($cmdClass, $profile->createCommand($cmdId));
}
/* ResponseQueued */
function testResponseQueued() {
$response = new \Predis\ResponseQueued();
$this->assertTrue($response->queued);
$this->assertEquals(\Predis\Protocol::QUEUED, (string)$response);
}
/* ResponseError */
function testResponseError() {
$errorMessage = 'ERROR MESSAGE';
$response = new \Predis\ResponseError($errorMessage);
$this->assertTrue($response->error);
$this->assertEquals($errorMessage, $response->message);
$this->assertEquals($errorMessage, (string)$response);
}
/* Connection */
function testConnection_StringCastReturnsIPAndPort() {
$connection = new \Predis\Connection(RC::getConnectionParameters());
$this->assertEquals(RC::SERVER_HOST . ':' . RC::SERVER_PORT, (string) $connection);
}
function testConnection_ConnectDisconnect() {
$connection = new \Predis\Connection(RC::getConnectionParameters());
$this->assertFalse($connection->isConnected());
$connection->connect();
$this->assertTrue($connection->isConnected());
$connection->disconnect();
$this->assertFalse($connection->isConnected());
}
function testConnection_WriteAndReadCommand() {
$cmd = \Predis\RedisServerProfile::getDefault()->createCommand('ping');
$connection = new \Predis\Connection(RC::getConnectionParameters());
$connection->connect();
$connection->writeCommand($cmd);
$this->assertTrue($connection->readResponse($cmd));
}
function testConnection_WriteCommandAndCloseConnection() {
$cmd = \Predis\RedisServerProfile::getDefault()->createCommand('quit');
$connection = new \Predis\Connection(new \Predis\ConnectionParameters(
RC::getConnectionArguments() + array('read_write_timeout' => 0.5)
));
$connection->connect();
$this->assertTrue($connection->isConnected());
$connection->writeCommand($cmd);
$connection->disconnect();
$exceptionMessage = 'Error while reading line from the server';
RC::testForCommunicationException($this, $exceptionMessage, function() use($connection, $cmd) {
$connection->readResponse($cmd);
});
}
function testConnection_GetSocketOpensConnection() {
$connection = new \Predis\Connection(RC::getConnectionParameters());
$this->assertFalse($connection->isConnected());
$this->assertType('resource', $connection->getSocket());
$this->assertTrue($connection->isConnected());
}
function testConnection_LazyConnect() {
$cmd = \Predis\RedisServerProfile::getDefault()->createCommand('ping');
$connection = new \Predis\Connection(RC::getConnectionParameters());
$this->assertFalse($connection->isConnected());
$connection->writeCommand($cmd);
$this->assertTrue($connection->isConnected());
$this->assertTrue($connection->readResponse($cmd));
}
function testConnection_RawCommand() {
$connection = new \Predis\Connection(RC::getConnectionParameters());
$this->assertEquals('PONG', $connection->rawCommand("PING\r\n"));
}
function testConnection_Alias() {
$connection1 = new \Predis\Connection(RC::getConnectionParameters());
$this->assertNull($connection1->getParameters()->alias);
$args = array_merge(RC::getConnectionArguments(), array('alias' => 'servername'));
$connection2 = new \Predis\Connection(new \Predis\ConnectionParameters($args));
$this->assertEquals('servername', $connection2->getParameters()->alias);
}
function testConnection_ConnectionTimeout() {
$timeout = 3;
$args = array('host' => '1.0.0.1', 'connection_timeout' => $timeout);
$connection = new \Predis\Connection(new \Predis\ConnectionParameters($args));
$start = time();
RC::testForCommunicationException($this, null, function() use($connection) {
$connection->connect();
});
$this->assertEquals((float)(time() - $start), $timeout, '', 1);
}
function testConnection_ReadTimeout() {
$timeout = 1;
$args = array_merge(RC::getConnectionArguments(), array('read_write_timeout' => $timeout));
$cmdFake = \Predis\RedisServerProfile::getDefault()->createCommand('ping');
$connection = new \Predis\Connection(new \Predis\ConnectionParameters($args));
$expectedMessage = 'Error while reading line from the server';
$start = time();
RC::testForCommunicationException($this, $expectedMessage, function() use($connection, $cmdFake) {
$connection->readResponse($cmdFake);
});
$this->assertEquals((float)(time() - $start), $timeout, '', 1);
}
/* ResponseReader */
function testResponseReader_OptionIterableMultiBulkReplies() {
$connection = new \Predis\Connection(RC::getConnectionParameters());
$responseReader = $connection->getResponseReader();
$responseReader->setHandler(
\Predis\Protocol::PREFIX_MULTI_BULK,
new \Predis\ResponseMultiBulkHandler()
);
$this->assertType('array', $connection->rawCommand("KEYS *\r\n"));
$responseReader->setHandler(
\Predis\Protocol::PREFIX_MULTI_BULK,
new \Predis\ResponseMultiBulkStreamHandler()
);
$this->assertType('\Iterator', $connection->rawCommand("KEYS *\r\n"));
}
function testResponseReader_OptionExceptionOnError() {
$connection = new \Predis\Connection(RC::getConnectionParameters());
$responseReader = $connection->getResponseReader();
$connection->rawCommand("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n");
$rawCmdUnexpected = "*3\r\n$5\r\nLPUSH\r\n$3\r\nkey\r\n$5\r\nvalue\r\n";
$responseReader->setHandler(
\Predis\Protocol::PREFIX_ERROR,
new \Predis\ResponseErrorSilentHandler()
);
$errorReply = $connection->rawCommand($rawCmdUnexpected);
$this->assertType('\Predis\ResponseError', $errorReply);
$this->assertEquals(RC::EXCEPTION_WRONG_TYPE, $errorReply->message);
$responseReader->setHandler(
\Predis\Protocol::PREFIX_ERROR,
new \Predis\ResponseErrorHandler()
);
RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function()
use ($connection, $rawCmdUnexpected) {
$connection->rawCommand($rawCmdUnexpected);
});
}
/* Client + CommandPipeline */
function testCommandPipeline_Simple() {
$client = RC::getConnection();
$client->flushdb();
$pipe = $client->pipeline();
$this->assertType('\Predis\CommandPipeline', $pipe);
$this->assertType('\Predis\CommandPipeline', $pipe->set('foo', 'bar'));
$this->assertType('\Predis\CommandPipeline', $pipe->set('hoge', 'piyo'));
$this->assertType('\Predis\CommandPipeline', $pipe->mset(array(
'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
)));
$this->assertType('\Predis\CommandPipeline', $pipe->mget(array(
'foo', 'hoge', 'foofoo', 'hogehoge'
)));
$replies = $pipe->execute();
$this->assertType('array', $replies);
$this->assertEquals(4, count($replies));
$this->assertEquals(4, count($replies[3]));
$this->assertEquals('barbar', $replies[3][2]);
}
function testCommandPipeline_FluentInterface() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->pipeline()->ping()->set('foo', 'bar')->get('foo')->execute();
$this->assertType('array', $replies);
$this->assertEquals('bar', $replies[2]);
}
function testCommandPipeline_CallableAnonymousBlock() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->pipeline(function($pipe) {
$pipe->ping();
$pipe->set('foo', 'bar');
$pipe->get('foo');
});
$this->assertType('array', $replies);
$this->assertEquals('bar', $replies[2]);
}
function testCommandPipeline_ClientExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
RC::testForClientException($this, 'TEST', function() use($client) {
$client->pipeline(function($pipe) {
$pipe->ping();
$pipe->set('foo', 'bar');
throw new \Predis\ClientException("TEST");
});
});
$this->assertFalse($client->exists('foo'));
}
function testCommandPipeline_ServerExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$client->getResponseReader()->setHandler('-', new \Predis\ResponseErrorSilentHandler());
$replies = $client->pipeline(function($pipe) {
$pipe->set('foo', 'bar');
$pipe->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
$pipe->set('hoge', 'piyo');
});
$this->assertType('array', $replies);
$this->assertType('\Predis\ResponseError', $replies[1]);
$this->assertTrue($client->exists('foo'));
$this->assertTrue($client->exists('hoge'));
}
function testCommandPipeline_Flush() {
$client = RC::getConnection();
$client->flushdb();
$pipe = $client->pipeline();
$pipe->set('foo', 'bar')->set('hoge', 'piyo');
$pipe->flushPipeline();
$pipe->ping()->mget(array('foo', 'hoge'));
$replies = $pipe->execute();
$this->assertType('array', $replies);
$this->assertEquals(4, count($replies));
$this->assertEquals('bar', $replies[3][0]);
$this->assertEquals('piyo', $replies[3][1]);
}
/* Client + MultiExecBlock */
function testMultiExecBlock_Simple() {
$client = RC::getConnection();
$client->flushdb();
$multi = $client->multiExec();
$this->assertType('\Predis\MultiExecBlock', $multi);
$this->assertType('\Predis\MultiExecBlock', $multi->set('foo', 'bar'));
$this->assertType('\Predis\MultiExecBlock', $multi->set('hoge', 'piyo'));
$this->assertType('\Predis\MultiExecBlock', $multi->mset(array(
'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
)));
$this->assertType('\Predis\MultiExecBlock', $multi->mget(array(
'foo', 'hoge', 'foofoo', 'hogehoge'
)));
$replies = $multi->execute();
$this->assertType('array', $replies);
$this->assertEquals(4, count($replies));
$this->assertEquals(4, count($replies[3]));
$this->assertEquals('barbar', $replies[3][2]);
}
function testMultiExecBlock_FluentInterface() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->multiExec()->ping()->set('foo', 'bar')->get('foo')->execute();
$this->assertType('array', $replies);
$this->assertEquals('bar', $replies[2]);
}
function testMultiExecBlock_CallableAnonymousBlock() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->multiExec(function($multi) {
$multi->ping();
$multi->set('foo', 'bar');
$multi->get('foo');
});
$this->assertType('array', $replies);
$this->assertEquals('bar', $replies[2]);
}
function testMultiExecBlock_EmptyCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->multiExec(function($multi) { });
$this->assertEquals(0, count($replies));
}
function testMultiExecBlock_ClientExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
RC::testForClientException($this, 'TEST', function() use($client) {
$client->multiExec(function($multi) {
$multi->ping();
$multi->set('foo', 'bar');
throw new \Predis\ClientException("TEST");
});
});
$this->assertFalse($client->exists('foo'));
}
function testMultiExecBlock_ServerExceptionInCallableBlock() {
$client = RC::getConnection();
$client->flushdb();
$client->getResponseReader()->setHandler('-', new \Predis\ResponseErrorSilentHandler());
$replies = $client->multiExec(function($multi) {
$multi->set('foo', 'bar');
$multi->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
$multi->set('hoge', 'piyo');
});
$this->assertType('array', $replies);
$this->assertType('\Predis\ResponseError', $replies[1]);
$this->assertTrue($client->exists('foo'));
$this->assertTrue($client->exists('hoge'));
}
function testMultiExecBlock_Discard() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->multiExec(function($multi) {
$multi->set('foo', 'bar');
$multi->discard();
$multi->set('hoge', 'piyo');
});
$this->assertEquals(1, count($replies));
$this->assertFalse($client->exists('foo'));
$this->assertTrue($client->exists('hoge'));
}
function testMultiExecBlock_DiscardEmpty() {
$client = RC::getConnection();
$client->flushdb();
$replies = $client->multiExec(function($multi) {
$multi->discard();
});
$this->assertEquals(0, count($replies));
}
function testMultiExecBlock_Watch() {
$client1 = RC::getConnection();
$client2 = RC::getConnection(true);
$client1->flushdb();
RC::testForAbortedMultiExecException($this, function()
use($client1, $client2) {
$client1->multiExec(array('watch' => 'sentinel'), function($multi)
use ($client2) {
$multi->set('sentinel', 'client1');
$multi->get('sentinel');
$client2->set('sentinel', 'client2');
});
});
$this->assertEquals('client2', $client1->get('sentinel'));
}
}
?>
+81 -10
View File
@@ -21,19 +21,33 @@ class RC {
const EXCEPTION_NO_SUCH_KEY = 'no such key';
const EXCEPTION_OUT_OF_RANGE = 'index out of range';
const EXCEPTION_INVALID_DB_IDX = 'invalid DB index';
const EXCEPTION_VALUE_NOT_INT = 'value is not an integer';
const EXCEPTION_EXEC_NO_MULTI = 'EXEC without MULTI';
const EXCEPTION_SETEX_TTL = 'invalid expire time in SETEX';
const EXCEPTION_HASH_VALNOTINT = 'hash value is not an integer';
private static $_connection;
public static function getConnectionArguments() {
return array('host' => RC::SERVER_HOST, 'port' => RC::SERVER_PORT);
}
public static function getConnectionParameters() {
return new Predis\ConnectionParameters(array('host' => RC::SERVER_HOST, 'port' => RC::SERVER_PORT));
}
private static function createConnection() {
$serverProfile = Predis\RedisServerProfile::get('dev');
$connection = new Predis\Client(array('host' => RC::SERVER_HOST, 'port' => RC::SERVER_PORT), $serverProfile);
$connection = new Predis\Client(RC::getConnectionArguments(), $serverProfile);
$connection->connect();
$connection->selectDatabase(RC::DEFAULT_DATABASE);
$connection->select(RC::DEFAULT_DATABASE);
return $connection;
}
public static function getConnection() {
public static function getConnection($new = false) {
if ($new == true) {
return self::createConnection();
}
if (self::$_connection === null || !self::$_connection->isConnected()) {
self::$_connection = self::createConnection();
}
@@ -105,25 +119,66 @@ class RC {
$thrownException = $exception;
}
$testcaseInstance->assertType('Predis\ServerException', $thrownException);
$testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
if (isset($expectedMessage)) {
$testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
}
}
public static function testForClientException($testcaseInstance, $expectedMessage, $wrapFunction) {
$thrownException = null;
try {
$wrapFunction($testcaseInstance);
}
catch (Predis\ClientException $exception) {
$thrownException = $exception;
}
$testcaseInstance->assertType('Predis\ClientException', $thrownException);
if (isset($expectedMessage)) {
$testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
}
}
public static function testForCommunicationException($testcaseInstance, $expectedMessage, $wrapFunction) {
$thrownException = null;
try {
$wrapFunction($testcaseInstance);
}
catch (Predis\CommunicationException $exception) {
$thrownException = $exception;
}
$testcaseInstance->assertType('Predis\CommunicationException', $thrownException);
if (isset($expectedMessage)) {
$testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
}
}
public static function testForAbortedMultiExecException($testcaseInstance, $wrapFunction) {
$thrownException = null;
try {
$wrapFunction($testcaseInstance);
}
catch (Predis\AbortedMultiExec $exception) {
$thrownException = $exception;
}
$testcaseInstance->assertType('Predis\AbortedMultiExec', $thrownException);
}
public static function pushTailAndReturn(Predis\Client $client, $keyName, Array $values, $wipeOut = 0) {
if ($wipeOut == true) {
$client->delete($keyName);
$client->del($keyName);
}
foreach ($values as $value) {
$client->pushTail($keyName, $value);
$client->rpush($keyName, $value);
}
return $values;
}
public static function setAddAndReturn(Predis\Client $client, $keyName, Array $values, $wipeOut = 0) {
if ($wipeOut == true) {
$client->delete($keyName);
$client->del($keyName);
}
foreach ($values as $value) {
$client->setAdd($keyName, $value);
$client->sadd($keyName, $value);
}
return $values;
}
@@ -131,12 +186,28 @@ class RC {
public static function zsetAddAndReturn(Predis\Client $client, $keyName, Array $values, $wipeOut = 0) {
// $values: array(SCORE => VALUE, ...);
if ($wipeOut == true) {
$client->delete($keyName);
$client->del($keyName);
}
foreach ($values as $value => $score) {
$client->zsetAdd($keyName, $score, $value);
$client->zadd($keyName, $score, $value);
}
return $values;
}
public static function getConnectionParametersArgumentsArray() {
return array(
'host' => '10.0.0.1', 'port' => 6380, 'connection_timeout' => 10, 'read_write_timeout' => 30,
'database' => 5, 'password' => 'dbpassword', 'alias' => 'connection_alias'
);
}
public static function getConnectionParametersArgumentsString($arguments = null) {
// TODO: must be improved
$args = $arguments ?: RC::getConnectionParametersArgumentsArray();
$paramsString = "redis://{$args['host']}:{$args['port']}/";
$paramsString .= "?connection_timeout={$args['connection_timeout']}&read_write_timeout={$args['read_write_timeout']}";
$paramsString .= "&database={$args['database']}&password={$args['password']}&alias={$args['alias']}";
return $paramsString;
}
}
?>
+905 -320
View File
File diff suppressed because it is too large Load Diff