Compare commits

...

213 Commits

Author SHA1 Message Date
Till Krüss ff59f74581 bump version 2022-10-11 09:52:29 -07:00
Mehmet Tolga Avcioglu 1a0eb7ee85 sentinel default to no password when replica authentication is used (#807)
* set default nopass if password is not provided

* remove debug output

* added tests for noauth sentinel

* Update SentinelReplication.php

Co-authored-by: Mehmet Tolga Avcioglu <mehmet.avcioglu@pusula.net.tr>
Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-10-06 08:14:47 -07:00
Tobias Bengtsson bb6c1a7b6d Add partial support for Stream commands (#804)
Adds support for the following commands not related to groups/consumers:
XADD, XDEL, XLEN, XRANGE, XREVRANGE, XTRIM.
2022-09-27 09:39:17 -07:00
streamingsystems 42c33a406f Check if socket is resource before reading (#802) 2022-09-18 15:01:02 -07:00
Stefano Borghi 7ad74217c2 check resource before writing, w/ test (#800) 2022-09-16 17:03:13 -07:00
Alexandru Pătrănescu 72d7675563 improve PHP 8.2 support (#797)
* use static::class . '::<method>' instead of 'static::method'

* extract concatenation so it can be improved easier at a later point

one improvement at some point could be to replace static::class . '::<method>' with static::<method>(...) in PHP 8.1+

Co-authored-by: Alexandru Patranescu <alexandru.patranescu@payu.com>
2022-09-08 11:25:37 -07:00
Till Krüss 8b5fa92856 mention php version [no ci] 2022-09-06 07:34:14 -07:00
Till Krüss 076a62e3d3 bump version to 2.0.2 2022-09-06 07:32:44 -07:00
Dries Vints 85dc1752b7 PHP 8.2 Support (#796) 2022-09-06 07:13:46 -07:00
Till Krüss 7683215023 bump tag to v2.0.1 2022-09-04 14:57:36 -07:00
Till Krüss dd4ddf6504 Update CHANGELOG.md 2022-08-03 11:53:59 -07:00
Till Krüss 6468e6cc2e Update CHANGELOG.md 2022-08-03 11:51:22 -07:00
digital2real 12f6d30502 Add retry logic to support temporary redis cluster failure (#788)
* Add retry logic to support temporary redis cluster failure

* formatting

* Use exponential backoff retries.
$minRetryAfter cant be changed if needed with RedisCluster::setMinRetryAfter

* fix formating

* (hotfix) formaing + import import  \Predis\Response\Error and \Predis\Connection\ConnectionException

* Update RedisCluster.php

* formatting

* tweaks

* Update RedisCluster.php

* Update RedisCluster.php

* Update RedisCluster.php

* Update unit tests regarding renaming of "retryInterval"

* spacing

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-08-03 11:49:56 -07:00
Linc a602fc7425 Fix sentinel reconnect issue on long-running PHP processes (#784)
* Fix no sentinel server available for autodiscovery on Swoole

* Fix errors in PHPUnit 9.5

the reason: https://github.com/sebastianbergmann/phpunit/commit/68582043e149039cfa3596b42ed35753dcf54fb2?fbclid=IwAR1qy1jFNFeJ3hUt5k7XAflqyCTUTWZhEqVSeizkybVHUEqXjBZ1gJM739U

* add restart sentinel test
2022-07-12 09:44:28 -07:00
John Paul E. Balandan, CPA 4ec74cc5fe Declare $parameters property of Predis\Connection\Parameters (#781) 2022-06-29 07:58:14 -07:00
BrightQi 78fd4cb998 bugfix for srem type to accept array #779 (#780)
* bugfix for srem  type to accept array #779

* add test to test SREM accept members as array type #779
2022-06-28 08:23:35 -07:00
Tomáš Fedor 2a8dd6a7f1 Add array as possible return type to spop (#774)
* Add array as possible return type to spop

* Update ClientInterface.php

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-06-14 19:59:10 -07:00
Jayin Taung 7d7b3416a8 Add example for installing predis by Composer (#772)
* Add example for installing predis by Composer

* Update README.md

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-06-13 07:54:46 -07:00
Till Krüss 99c253733d tweak changelog format 2022-06-08 06:14:56 -07:00
Till Krüss 69bb8fa082 bump version to v2.0.0 stable 2022-06-08 06:11:35 -07:00
Josias Montag e5221fa13b Allow PubSub / MultiExec with Replication 2022-05-28 08:18:01 -07:00
Till Krüss 15244c9642 cleanup readme 2022-05-26 12:01:30 -07:00
Till Krüss 4cc119a03c tag 2.0.0-beta.1 2022-05-26 11:48:12 -07:00
Till Krüss 619890f715 fix test 2022-05-26 11:45:35 -07:00
Till Krüss b060d43157 port changes from v1.1 branch 2022-05-26 11:29:45 -07:00
Till Krüss a5ab2bce6b truncate changelog 2022-05-26 11:12:41 -07:00
Hubert Lenoir 89f20dc968 add TOUCH command (#767) 2022-05-23 08:46:48 -07:00
Michal Lulco 6af5394a89 Added ZPOPMIN and ZPOPMAX (#758)
* Added ZPOPMIN and ZPOPMAX

* Added ZPOPMIN and ZPOPMAX

* Applied patch
2022-05-16 08:00:06 -07:00
Zaher Ghaibeh c508086b0e remove redundant variables (#765) 2022-05-15 09:51:28 -07:00
Zaher Ghaibeh 3c01ccc94c Testing code against Redis 7 (#764)
* redis 7 init

fix typo

add the new returns

* having a full sentence for the exception message
2022-05-12 12:23:51 -07:00
Jordi Boggiano e382bedb27 Update method tags (#762) 2022-05-12 09:13:06 -07:00
Zaher Ghaibeh a2449da7c8 PHP 8.1 tests (#760)
* - init 8.1 tests
- adding `#[\ReturnTypeWillChange]`
- Fix deprecation message: strlen(): Passing null to parameter #1 ($string) of type string is deprecated
- null is not allowed
- cleanup
- default values should be same type

* Mirror `v1.1` fixes for these files

* add null as type too since we also accept null as default value

Signed-off-by: Zaher Ghaibeh <zaher@zaher.dev>

* revert back to assertNull

* Update CHANGELOG

* Update CHANGELOG.md

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-05-07 08:03:53 -07:00
♚ PH⑦ de Soria™♛ b3a5bdf940 Remove invalid input for actions/cache (#737)
https://github.com/actions/cache/blob/main/examples.md#php---composer
2022-01-14 08:22:59 -08:00
Georges.L 4c1aada5ae Updated set, setex, setexp type hint (#710) 2021-09-08 09:05:55 -07:00
Mehdi Baaboura 54775a0295 Update LICENSE (#704) 2021-07-19 09:27:37 -07:00
Ruslan Bekenev 367bd72a0b Test optional count argument for LPOP (#700)
First added in Redis 6.2
2021-06-09 09:46:24 -07:00
Till Krüss 1d7ccb64ab Merge pull request #670 from charescape/patch-1
Update README.md
2021-04-04 10:33:08 -07:00
Till Krüss e29ae9065f some spacing 2021-04-04 10:31:53 -07:00
Till Krüss 64fc7774c6 stick to PHPUnit 9.4.* for now
`getAnnotations()` was removed in 9.5:

https://github.com/sebastianbergmann/phpunit/commit/68582043e149039cfa3596b42ed35753dcf54fb2
2021-04-04 10:31:45 -07:00
Till Krüss 554c36b329 Merge pull request #688 from open-source-contributions/improve_assertions
Improve PHPUnit assertions
2021-04-04 10:20:39 -07:00
Till Krüss be9d477564 fix for Redis 6 error message 2021-04-04 10:16:44 -07:00
Till Krüss 0e4e8f41cc fix 2021-04-04 10:14:13 -07:00
Till Krüss aa0c30809b fix test when using Redis 6 2021-04-04 10:11:29 -07:00
Peter 20f8faee6f Improve PHPUnit assertions 2021-04-04 23:30:14 +08:00
Till Krüss aa436263f8 Merge pull request #684 from andrew-demb/patch-2
CommandInterface::parseResponse() can accept array and null argument
2021-03-09 08:43:59 -08:00
Till Krüss 3c8856b05a Merge pull request #683 from andrew-demb/patch-1
Status::get() always return self
2021-03-09 08:43:39 -08:00
Andrii Dembitskyi d935f6e5c2 CommandInterface::parseResponse() can accept array and null argument 2021-03-09 07:15:08 -05:00
Andrii Dembitskyi 87fcc69e7a Status::get() always return self 2021-03-09 07:09:15 -05:00
charescape 407ddeca92 Update README.md 2020-11-12 11:39:48 +08:00
Till Krüss 338ba6d73d Merge pull request #665 from danwall/patch-1
Fix typo
2020-09-26 19:08:13 -07:00
Dan Wall 25db9bfec5 Fix typo 2020-09-26 11:52:44 +10:00
Daniele Alessandri 0e9aeee90f Fix PhpiredisSocketConnection for PHP 8.
Removed is_resource() check as socket_create() now returns an object.
2020-09-20 15:29:16 +02:00
Daniele Alessandri f07f8d01fe Where the heck did my beloved flat-style for badges go? 2020-09-10 21:51:36 +02:00
Daniele Alessandri f133a3660c Various updates to options class and interface. 2020-09-10 21:23:45 +02:00
Daniele Alessandri fa44ff0846 Improve code used for client initialization.
Also provide a more detailed description of methods in phpdocs.
2020-09-10 21:23:10 +02:00
Daniele Alessandri 985343a992 Merge remote-tracking branch 'github/pr/656' into main
Local branch v2.0-clientoptions-aggregateconnections
2020-09-10 21:19:52 +02:00
Daniele Alessandri d954e9f43f Merge branch 'v1.1' into main 2020-09-10 21:16:58 +02:00
Daniele Alessandri 9e5371f5ce Back to development.
[ci skip]
2020-09-10 15:40:46 +02:00
Daniele Alessandri 8ca99f91e4 Update CHANGELOG and bump VERSION (v1.1.5) 2020-09-10 15:35:23 +02:00
Qusonann c778843b0d Fix zrevrangebyscore pipeline annotation
[ci skip]
2020-09-09 23:42:03 +02:00
Daniele Alessandri b3b81fce9f Merge remote-tracking branch 'github/pr/657' into main 2020-09-09 23:39:18 +02:00
Qusonann ccc2ea24cb Fix zrevrangebyscore pipeline annotation 2020-09-08 21:23:35 +03:00
Daniele Alessandri dca201291c Fix CHANGELOG for broken Markdown rendering.
[ci skip]
2020-09-05 16:50:25 +02:00
Daniele Alessandri 2e76410e65 Support password-based authentication for sentinel instances.
Password-based authentication for sentinels has been added in Redis 5.
Predis was actively ignoring any "password" parameter for sentinels when
creating connections to them to avoid issues when this parameter is set
in the default "parameters" array passed via client options, as they are
applied to **every** connection created by Predis (see #346).

We need to find a better way to specify a common password for sentinels
to be handled in a different way than the ones for Redis nodes. For now
each sentinel node protected by password must have an explicit password
set in its parameters list even if this password, by design, is the same
for all sentinels. Since we cannot use default "parameters" as explained
above but we still need to pass a common value for all sentinels an idea
could be using a dedicated client option like we did with "service", but
we will see later.

In this commit we also explicitly reset any "username" parameter as it
would trigger an `AUTH $username $password` but sentinels do not support
ACL authentication.

Fixes #594.
2020-09-05 16:25:06 +02:00
Daniele Alessandri ca468b785c Throw exception on FALSE passed to "replication" option.
While "replication" do accept values evaluating to TRUE, the same cannot
be said for values evaluating to FALSE. TRUE is used to tell the client
that we want replication handled using the default backend for unmanaged
replication setups. For using redis-sentinel the "sentinel" string value
must be passed.

Setting "replication" to FALSE led to a failure (and a PHP warning) on
client initialization because this condition was not handled properly.
Being able to do so would not make sense anyway: when the client does
not need to be set up to rely on replication, users simply have to omit
the option. Furthermore, users must always specify either "replication"
or "cluster" and not both with one of them set to FALSE.

Unfortunately options for aggregate connections in Predis v1.1 are a bit
of a mess, they did not scale well with the addition of new features and
are also quite inconsistent (e.g. "cluster" does not accept TRUE).

This has been largely fixed in Predis v2.0-dev but required implementing
a few breaking changes. It also means that this change does not need to
be ported to the main branch.

Addresses #381 using a different approach.
2020-09-05 14:18:30 +02:00
Daniele Alessandri b1ea4374d4 [tests] Fix the very few remaining warnings emitted by test suite.
Basically assertMatchesRegularExpression() replaces assertRegExp() which
has been deprecated since PHPUnit 9.1 and will be removed in PHPUnit 10,
unfortunately we still depend on PHPUnit 8.4 to support PHP 7.2 and this
version does not have assertMatchesRegularExpression() so we implemented
it in our base testcase class with a fallback to the old assertRegExp()
when tests are executed on older versions of PHPUnit.
2020-09-04 21:00:46 +02:00
Daniele Alessandri 4a7fb55058 [tests] Add utility method to skip tests on CI environment.
We still have disabled all PUB/SUB related tests on CI for now, until we
understand why they fail at random.

Backported from main branch (ref. 5133706, f723f67, eb8a89e)
2020-09-04 18:42:38 +02:00
Daniele Alessandri fdf663ebba Fix WebdisConnection with IPv4 hosts.
Apparently something changed since last time in cURL internals and now
an IPv4 wrapped by square brackets (which are used for IPv6 addresses)
returns a malformed request error.

m
2020-09-04 18:06:36 +02:00
Daniele Alessandri 880ad09cad Restore broken change from a few years ago.
After this change there was no check to prevent the user from using SSL
with PhpiredisStreamConnection (it does not work due to the fact that
internally is uses stream_socket_recvfrom()).

Pretty obvious phpiredis was not installed on my system when I made that
change or the test suite would have caught the wrong behaviour (like it
just happened now that I build it from scratch).
2020-09-04 18:05:26 +02:00
Daniele Alessandri e5f44c33a2 No need for socket_import_stream() to set TCP_NODELAY since PHP 7.1. 2020-09-04 17:56:19 +02:00
Daniele Alessandri 67487528eb Fix WebdisConnection with IPv4 hosts.
Apparently something changed since last time in cURL internals and now
an IPv4 wrapped by square brackets (which are used for IPv6 addresses)
returns a malformed request error.
2020-09-04 17:53:16 +02:00
Daniele Alessandri ac543999ef Restore broken change from a few years ago.
After this change there was no check to prevent the user from using SSL
with PhpiredisStreamConnection (it does not work due to the fact that
internally is uses stream_socket_recvfrom()).

Pretty obvious phpiredis was not installed on my system when I made that
change or the test suite would have caught the wrong behaviour (like it
just happened now that I build it from scratch).
2020-09-04 17:19:57 +02:00
Daniele Alessandri bffa1d020b [tests] Just a lone leftover... 2020-09-04 13:54:51 +02:00
Daniele Alessandri 9e48aef4b0 [tests] Adapt after changes in aggregate connection initialization.
See commit dd5d665 for the above mentioned changes.

Tests for Option\Aggregate, Option\Replication and Option\Cluster should
be refactored because code is a bit too repetitive and a bunch of tests
are shared among them (replication and cluster options extend aggregate
and share the same logic after all).
2020-09-03 15:12:34 +02:00
Daniele Alessandri dd5d665156 Dramatically improve aggregate connections initialization.
This is a complete overhaul of how aggregate connections are created and
initialized, now everything is self-contained in our usual 3 supported
client options: "aggregate", "cluster" and "replication".

The usage of callables acting as connection initializerss is now more
consistent through the various options. When the callable is invoked it
receives 3 arguments (the original set of connection parameters passed
by reference, the options container, the current option) and must return
an instance of Predis\Connection\AggregateConnectionInterface otherwise
an InvalidArgumentException is thrown.

When using "cluster" and "replication" the returned aggregate connection
is automatically populated by adding the list of nodes in $parameters,
on the other hand "aggregate" skips this automatism so it is up to the
user. In any case the user-supplied callable receives $parameters as a
reference, setting $parameters to NULL inside the body of the callable
makes the client skip automatic aggregation regardless of the option in
use.

In addition to this the actual procedure of adding nodes to an aggregate
connection has been moved directly into the respective options instead
of being spread between the client (which instead should just pass a set
of parameters and get back a fully-configured aggregate connection) and
the connection factory (and the scope of a connection factory is only to
create new connetion instances to single Redis servers).
2020-09-03 12:43:15 +02:00
Daniele Alessandri efbe80222e Merge remote-tracking branch 'github/pr/655' into main
Local branch v2.0-commandfactory-raw
2020-09-03 12:41:00 +02:00
Daniele Alessandri eb8a89ee7a [tests] Skip integration tests for DispatcherLoop on CI.
Same reason as of PubSub/Consumer, still need to investigate the random
failures. Anyone can help?
2020-09-03 12:33:12 +02:00
Daniele Alessandri f723f67c3f [tests] Use new utility method to skip PUB/SUB tests on CI. 2020-09-03 12:32:38 +02:00
Daniele Alessandri 5133706c36 [tests] Add utility method to skip tests on CI environment. 2020-09-03 12:31:53 +02:00
Daniele Alessandri ae1befb740 Merge remote-tracking branch 'github/pr/654' into main
Local branch v2.0-testsuite-improvements
2020-09-03 12:10:47 +02:00
Daniele Alessandri 0fdca9e805 Remove check for PHP <= 5.4 from Predis\Session\Handler. 2020-09-01 20:39:34 +02:00
Daniele Alessandri eabbd100c9 No need to check for PHP >= 7.0 anymore for persistent SSL.
Leaving assertParameters() for now but in general we should review how
connection backends are initialized to simplify things and remove some
protected methods, at least from base classes.
2020-09-01 20:21:11 +02:00
Daniele Alessandri 1285951243 Predis\Command\RawCommand is now a final class.
The behaviour of a raw command should not ever be overridden by users,
so this change is actually to enforce consistency through the library.
For the most part, raw commands are used internally by Predis to handle
management commands such as CLUSTER SLOTS, ROLE, etc...
2020-09-01 14:45:39 +02:00
Daniele Alessandri 6d6c970ae2 [tests] Replace deprecated TestCase::at().
The "at" matcher will be removed in PHPUnit 10 but it is not a bad thing
after all because it was cumbersome and error-prone.

Took the opportunity to improve some tests while converting them.
2020-09-01 14:03:19 +02:00
Daniele Alessandri c6f2ce4b83 [tests] Replace generic MockObject::will() with specialized methods. 2020-09-01 14:03:19 +02:00
Daniele Alessandri 36c9bb047e [tests] Start improving test suite.
- Make use of more typehints for function parameters
- Make use of typehints for function return values
- Use @var where needed to give proper hints to IDEs and avoid warnings
- Replace MockObject::setMethods() with addMethods() and onlyMethods()
- Rewording of some phpdocs
2020-09-01 13:58:14 +02:00
Daniele Alessandri 5eccc65d42 Fix handling of username when using "redis://".
The username is now correctly retrieved from the userinfo fragment of
the URI when using the "redis" scheme and a "username:password" pair is
present. Values retrieved from the userinfo fragment always override the
ones specified in `username` and `password` if those fields are present
in the query string.
2020-09-01 11:40:30 +02:00
Daniele Alessandri f225c55a01 Back to development.
[ci skip]
2020-08-31 12:39:52 +02:00
Daniele Alessandri c2ede30394 Merge remote-tracking branch 'github/pr/653' into main
Local branch v2.0-connections-phpiredisconfig
2020-08-31 10:54:40 +02:00
Daniele Alessandri c2c1812b87 Merge remote-tracking branch 'github/pr/651' into main
Local branch v2.0-commandfactory-reworkinterface
2020-08-31 10:54:19 +02:00
Daniele Alessandri 743be10f6e Merge remote-tracking branch 'github/pr/650' into main
Local branch v2.0-parameters-filterempty
2020-08-31 10:53:43 +02:00
Daniele Alessandri 7b86a7b54d Merge branch 'v1.1' into main 2020-08-30 23:40:36 +02:00
Daniele Alessandri dc067b3dac Extend "commands" option to support Predis\Command\RawFactory.
The option handler has been extended to accept descriptive string values
in order to create and configure an appropriate command factory.

Accepted string values are:

- "predis" creates the usual command factory
- "raw" creates a raw command factory
- "default" is simply an alias of "predis"
2020-08-30 15:24:12 +02:00
Daniele Alessandri 30a065ad86 Implement Predis\Command\RawFactory.
Any command ID will produce a command instance even for unknown commands
not implemented by Redis (the server will return a -ERR error response).

When using this factory the client does not process arguments before
sending commands to Redis and server responses are not further processed
before being returned to the caller.
2020-08-30 15:01:43 +02:00
Daniele Alessandri bd68b55456 Merge remote-tracking branch 'github/pr/649' into main 2020-08-29 11:42:38 +02:00
Daniele Alessandri cc2a7657db Rework Predis\Command\FactoryInterface and related classes.
We have renamed most methods to drop the "command" suffix as it is quite
redundant. Due to this change and thanks to variadic methods introduced
with PHP 5.6 we took the opportunity to replace both "supportsCommand()"
and "supportsCommands()" with a single new method "supports()".

Added more stringent typehints for method arguments and typehints for
return values now that we do not need to support anything below PHP 7.2.
Also moved from using array() to [] in source code of class involved.
2020-08-28 14:23:12 +02:00
Daniele Alessandri 5348c4025e Merge remote-tracking branch 'github/pr/648' into main 2020-08-28 10:28:17 +02:00
Daniele Alessandri 5afadb560f [tests] Skip testPubSubAgainstRedisServer on CI environments.
See 04d5c10.
2020-08-27 17:38:38 +02:00
Daniele Alessandri e58d5fceef Add "redis" scheme in configuration for phpiredis.
Forgot about it! While "redis" is perceived simply as an alias for "tcp"
it must be explicitly mapped in the connection factory.
2020-08-27 16:38:38 +02:00
Daniele Alessandri 56d704f5dd Add "default" as accepted string for "connections".
Using "default" returns a connection factory instance with the default
configuration. Basically it is no different than omitting "connections"
in the client options array but it can be useful when applications want
to automatically configure Predis to use phpiredis when it is loaded:

$client = new Predis\Client('tcp://127.0.0.1', [
  'connections' =>
      extension_loaded('phpiredis')
        ? 'phpiredis'
        : 'default'
]);

This is in response to ISSUE #397.
2020-08-27 15:59:14 +02:00
Daniele Alessandri 2c732c46c8 Make configuration easier for phpiredis based connections.
The "connections" client option now accepts certain string values that
are mapped to specific and most used configurations for the connection
factory. This is used to make it easier to configure Predis to load our
phpiredis-based connection backends without having to manually pass a
map of URI schemes and fully-qualified class names.

  - "phpiredis-stream" maps `tcp` and `unix` to the connection backend
    based on PHP streams (Predis\Connection\PhpiredisStreamConnection).
  - "phpiredis-socket" maps `tcp` and `unix` to the connection backend
    based on ext-socket (Predis\Connection\PhpiredisStreamConnection).
  - `phpiredis` is simply an alias of `phpiredis-stream`.

An InvalidArgumentException is thrown on unsupported string values.
2020-08-27 15:09:35 +02:00
Daniele Alessandri accb030eb5 [tests] Minor adjustments for callable connection initalizers. 2020-08-26 00:57:03 +02:00
Daniele Alessandri 04d5c10193 [tests] Skip testPubSubAgainstRedisServerBlocking on CI environments.
NOTE: this specific test fails at random without any apparent reason
when executed on our CI environments and these failures are not tied
to a particular version of PHP or Redis. It is most likely some weird
timing issue on busy systems as it is really rare to get it triggered
locally. The chances it is a bug in the library are pretty low so for
now we just mark this test skipped on our CI environments (but still
enabled for local test runs) and "debug" this issue using a separate
branch to avoid having spurious failures on main development branches
which is utterly annoying.

We will restore this test on CI environments as soon as we understand
what is the reason behind its random failures.
2020-08-25 17:52:19 +02:00
Daniele Alessandri 4e605ea233 Revert "[tests] Try fixing random failures in one test of PUB/SUB abstraction."
Whoops, wrong test :)

This reverts commit 5c2b31c566.
2020-08-25 17:47:23 +02:00
Daniele Alessandri 5c2b31c566 [tests] Try fixing random failures in one test of PUB/SUB abstraction.
These failures are random and rarely reproducible on a local development
environment but sometimes they affect the success of a test run and it's
annoying. I think it is just a weird timing issue on busy hosts so let's
try with a couple of simple usleep() after SUBSCRIBE and PUBLISH and see
if anything changes in the next few test runs.
2020-08-25 17:16:19 +02:00
Daniele Alessandri 030e8a080c Remove references to Phar from README.
We have removed the utility script for building phar archives out of the
repository, but if you still need to build a phar archive for Predis you
should still be able to use "./bin/create-phar" from the v1.1 branch.
2020-08-25 16:38:04 +02:00
Daniele Alessandri 15eadb3426 Merge branch 'v1.1' into main 2020-08-25 16:07:06 +02:00
Daniele Alessandri b014d5de5a Filter NULL and zero-length string values for parameters.
Having NULL values or zero-length strings for connection parameters does
not make much sense and actually it proved to be an issue with certain
parameters like "password" where an empty string would trigger an AUTH
command with an empty password (and obviously Redis was not happy with
that). The main offenders were a few libraries and frameworks that kept
passing empty values for parameters such as "database" and "password"
even when users left them unconfigured. This fix should make things more
robust and avoid such occurrences in the future.

Related to PR #436 (rejected).
2020-08-25 12:20:53 +02:00
Daniele Alessandri 78041126fc Merge remote-tracking branch 'github/pr/646' into main 2020-08-24 22:43:21 +02:00
Daniele Alessandri 7113e31954 Merge branch 'v2.0-merge-into-main' into main 2020-08-24 22:41:44 +02:00
Daniele Alessandri 6441f34ec5 Merge branch 'v1.1' into main 2020-08-24 22:36:38 +02:00
Daniele Alessandri 0ffaa864e1 Apply few changes in .gitattributes file.
Remove .github from directories exported in our release packages and fix
to ignore .php_cs.dist (the actual file commited) instead of .php_cs.

This also reverts commit 7489690 (slipped into main after merging v1.1),
we do not need it since there is no patching going on for PHPUnit after
our target version was bumped to ^8.0 || ^9.0 on main.
2020-08-24 14:00:05 +02:00
Daniele Alessandri c662ec7b2a Merge branch 'v1.1' into main 2020-08-24 12:03:34 +02:00
Daniele Alessandri f06a41cfdc Change method to undefine commands in factory.
The previous implementation was not good because we were exposing in the
public API an internal implementation detail of the base factory class,
furthermore it made Predis\Command\Factory::define() confusing. Having a
separate method to undefine commands in the factory is self-explanatory.

We also changed `Predis\Configuration\Option\Commands` accordingly when
a dictionary of $commandID => $classCommand is passed to the "commands"
client option and $classCommand is NULL.

A few minor changes (mostly cosmetic or documentation) were applied too.

From feedback to PR #644.
2020-08-21 14:30:52 +02:00
Daniele Alessandri 5a7548bc81 Remove useless argument in Predis\Client::getClientBy().
There was no real meaning to have a callback here, for the most part it
was just a leftover of a previous approach implemented with ee7104d and
quickly superseded by the current approach that simply returns the new
client instance instead of using callbacks.

From feedback to #644.
2020-08-21 14:00:13 +02:00
Daniele Alessandri d4bcf912f4 [tests] Drop TODO and implement type check in utility method.
From feedback to PR #644.
2020-08-21 11:20:24 +02:00
Daniele Alessandri a772f0b16b [tests] Drop now useless method.
From feedback to PR #644.
2020-08-21 11:20:07 +02:00
Anatoly Pashin 44cf08e691 Fix PHPUnit installation on PHP 8 2020-08-20 17:53:34 +10:00
Till Krüss 371b481f12 Merge pull request #641 from heithemmoumni/patch-1
use present for copyright range
2020-08-18 08:30:48 -07:00
Till Krüss d5106c9f00 Update LICENSE
Co-authored-by: Anatoly Pashin <anatoly.pashin@gmail.com>
2020-08-18 08:30:33 -07:00
Daniele Alessandri 4b2b35202a [tests] Restore new message for failed assertion. 2020-08-18 16:12:45 +02:00
Daniele Alessandri c26909699b Merge remote-tracking branch 'github/pr/639' into main 2020-08-18 16:03:10 +02:00
moumni 593920085e use present for copyright range 2020-08-17 20:58:03 +01:00
Daniele Alessandri 592dd91454 Fix class name for OBJECT command.
Starting with PHP 7.2 "object" is a reserved word and cannot be used as
a name for classes, interfaces or traits.
2020-08-17 18:32:14 +02:00
Daniele Alessandri 33f5a0ec8c Merge branch 'stale-master' into main 2020-08-17 17:43:13 +02:00
Raza Mehdi 36a02cf807 Fix test. 2020-08-16 04:31:52 +05:00
Raza Mehdi d6ee18bf21 Merge remote-tracking branch 'github/main' into main 2020-08-16 02:04:16 +05:00
Raza Mehdi 5edf443141 Fix tests. 2020-08-16 01:59:41 +05:00
Till Krüss bf777f3529 Merge pull request #626 from predis/remove-pear
Remove PEAR/PHAR
2020-08-14 10:40:13 -07:00
Till Krüss 390adfb3b8 Merge branch 'main' into remove-pear 2020-08-14 10:39:52 -07:00
Till Krüss 25b6b824b4 update badges 2020-08-14 10:35:25 -07:00
Till Krüss d4a170d890 drop Travis 2020-08-14 10:27:07 -07:00
Till Krüss 71b2119436 bump version 2020-08-14 10:15:57 -07:00
Till Krüss 7636d55e10 rename github org 2020-08-14 10:10:01 -07:00
Till Krüss 80191d6487 Merge pull request #634 from srmkliveforks/main
Setup Github Actions Workflows
2020-08-14 10:02:37 -07:00
Raza Mehdi 882d59001f Use PHPUnit 9. 2020-08-14 21:59:48 +05:00
Raza Mehdi f01a0cf5d0 Use specific php version. 2020-08-14 21:53:25 +05:00
Raza Mehdi 7d753f236a Move redis versions into matrix. 2020-08-14 18:36:08 +05:00
Raza Mehdi 7b1924528b Add Badge. 2020-08-14 17:51:06 +05:00
Raza Mehdi 9b355d1766 Use Redis 4 to 6. 2020-08-14 17:40:41 +05:00
Raza Mehdi 787d68f1c4 Set Github actions 2020-08-14 15:55:18 +05:00
Till Krüss ffadffb71c Merge pull request #628 from b1rdex/patch-1
Test on Redis 3-6
2020-08-13 08:45:29 -07:00
Till Krüss b637be9e4a ignore phpunit cache 2020-08-13 08:42:32 -07:00
Till Krüss 64937f725b get phpunit running 2020-08-13 08:41:43 -07:00
Till Krüss 44f98e6635 Merge pull request #631 from b1rdex/patch-2
Fix php version requirement
2020-08-13 08:08:26 -07:00
Anatoly Pashin 1eeef0e5d7 Change the php version constraint 2020-08-13 19:19:12 +10:00
Anatoly Pashin 5b4de508ba Fix php version requirement 2020-08-13 16:22:22 +10:00
Till Krüss c30c60d6f9 trigger travis
Travis CI wasn't set up during the move the `predishq`.
2020-08-12 19:56:40 -07:00
Anatoly Pashin 96e286aaf7 Test on Redis 3-6 2020-08-13 12:47:29 +10:00
Till Krüss 4edfda0669 remove single file script 2020-08-12 12:29:48 -07:00
Till Krüss 36e7cb487d remove patch files 2020-08-12 12:13:09 -07:00
Till Krüss 6cd919c43c remove pear stuff 2020-08-12 12:10:19 -07:00
Till Krüss 3104874887 Merge remote-tracking branch 'origin/v1.1' into main 2020-08-12 12:00:41 -07:00
Till Krüss 3a3a7fb349 use correct php 8 id 2020-08-12 11:57:53 -07:00
Till Krüss 3d68a59505 remove hhvm badge
# Conflicts:
#	README.md
2020-08-12 11:57:52 -07:00
Till Krüss c380b434b0 readme tweaks
# Conflicts:
#	README.md
2020-08-12 11:57:31 -07:00
Till Krüss ca6daea582 add code of conduct 2020-08-12 11:55:34 -07:00
Till Krüss c85434bbbb bump phpunit from 4 to 8
# Conflicts:
#	composer.json
2020-08-12 11:55:33 -07:00
Till Krüss fa73623c75 drop php 7.1 and older; add php 8
# Conflicts:
#	.travis.yml
2020-08-12 11:54:28 -07:00
Fabien Villepinte 3acf867cac Fix PHPDoc of \Predis\Connection\Aggregate\SentinelReplication::assertConnectionRole() 2016-11-22 15:33:03 +01:00
orvice 9b7897b2da fix typo 2016-11-22 15:31:43 +01:00
Remi Collet ce2cbfd929 fix test for ArgumentCountError exception with PHP 7.1 2016-11-17 18:13:12 +01:00
Remi Collet 0b3af06e6b enable 7.1 on travis 2016-11-17 18:13:00 +01:00
Daniele Alessandri ea28d82681 More updates to README to reflect changes in parameters handling.
[ci skip]
2016-06-21 10:51:35 +02:00
Daniele Alessandri bb81f67a0f Update README to reflect changes in parameters handling.
[ci skip]
2016-06-20 18:14:00 +02:00
Daniele Alessandri a4d63e2510 [tests] Fix preassigned slots in slotmap tests. 2016-06-18 21:54:01 +02:00
Daniele Alessandri 89840412d5 [tests] Fix preassigned slots in cluster tests. 2016-06-18 21:53:49 +02:00
Daniele Alessandri 67c0fb8eb1 Separate id and alias in predis cluster (client-side sharding).
This change does not affect distribution but simply separates the two
concepts of connection ID (ip:port pair) and alias (value set via the
"alias" connection parameter), the method getConnectionByAlias() has
been added to reflect this change.

The method getConnectionBySlot() has also been added.
2016-06-18 17:12:20 +02:00
Daniele Alessandri 0a7ddf7bba Fix spacing in phpdocs. 2016-06-18 15:52:15 +02:00
Daniele Alessandri 184d583895 Implement the concept of role for connections.
When using replication backends, now the role of a connection is not
defined by its alias but by the new connection parameter "role" that
can be set to "master", "slave" and (for redis-sentinel) "sentinel".

This also led to a redesign of how connections can be retrieved from
replication backends: the method getConnectionById() now retrieves a
connection only by its ID (ip:port pair), to get a connection by its
alias there is the new method getConnectionByAlias(). This method is
not supported by the redis-sentinel backend due to its dynamic nature
(connections are retrieved and initialized at runtime from sentinels)
but it is still possible to get a single connection from the pool by
using its ID. It is also possible to retrive a connection by its role
using the method getConnectionByRole().

NOTE: the "role" parameter is an hint for the internals of aggregate
connection backends so it is still possible that the actual role of
a connection changes during the execution of a script (e.g. a slave
gets promoted to the role of master): in this case the parameters of
that connection will not be changed as they are immutable, but the
method getConnectionByRole() will return the appropriate connection.

Predis\Client::getClientBy() has been updated with the addition of
"role" and "alias" as supported selectors.
2016-06-18 15:32:53 +02:00
Daniele Alessandri 69e40dab2b Extract slot map logic to separate class.
In addition to that, the methods askSlotsMap() and buildSlotsMap() of
the redis-cluster connection backend have been renamed respectively
to askSlotMap() and buildSlotMap().
2016-06-17 22:18:38 +02:00
Daniele Alessandri 028992cf1b Remove useless method. 2016-06-17 14:13:02 +02:00
Daniele Alessandri 8de97ffe1a Fix snippet for replication in README.
[ci skip]
2016-06-17 13:58:08 +02:00
Daniele Alessandri d38cf2788b Update CHANGELOG. 2016-06-17 12:38:56 +02:00
Daniele Alessandri 0b226ff1d5 [tests] Missing test case for redis-cluster strategy. 2016-06-17 12:32:32 +02:00
Daniele Alessandri e38df19fac Implement "crc16" client option for redis-cluster.
By default Predis chooses automatically which hash generator to use:
if a suitable version of ext-phpiredis is loaded (that is, a version
that implements the phpiredis_utils_crc16() function) it uses the new
Predis\Cluster\Hash\PhpiredisCRC16 generator, otherwise it falls back
to the usual pure-PHP Predis\Cluster\Hash\CRC16 generator.
2016-06-17 12:32:29 +02:00
Daniele Alessandri 509a330bca Implement CRC16 hash generator based on ext-phpiredis.
Generating the hash of a string of 10 bytes is 10 times faster than
the equivalent pure-PHP implementation of CRC16 (PHP 7.0.7).

This generator requires a version of phpiredis that implements the
function "phpiredis_utils_crc16()".
2016-06-17 12:32:29 +02:00
Daniele Alessandri 0c4a90d809 Update CHANGELOG.
[ci skip]
2016-06-16 19:14:41 +02:00
Daniele Alessandri b3b35a9ae2 Remove Predis\Client::getConnectionById(). 2016-06-16 16:10:46 +02:00
Daniele Alessandri 7bb2192294 Replace Predis\Client::on() with getClientBy().
This method improves the previous one, which worked only by picking a
connection by ID, allowing users to specify a selector type among the
following ones: "id", "key", "slot", "command". These selectors are
expanded internally to methods, invoked against the connection object
in use by the client, that follow the getConnectionByXXX() convention
already in use through the library:

  id      => getConnectionById()
  key     => getConnectionByKey()
  slot    => getConnectionBySlot()
  command => getConnectionByCommand()

The underlying connection does not necessarily need to implement the
aggregate connection interface as the client relies on a duck-typing
approach by using method_exists().
2016-06-16 16:10:42 +02:00
Daniele Alessandri 53b2cc340a Apply same fix of 8437fbc to ClientContextInterface. 2016-06-16 10:53:23 +02:00
Felix Sandström 99f1e3290e Correcting the PHPDocs 2016-06-16 10:53:23 +02:00
Daniele Alessandri 9d8adee1b1 Rename to getConnectionByCommand($command). 2016-06-16 10:39:44 +02:00
Daniele Alessandri a70971549e Fix GC issues with closures for phpiredis reader handlers.
The following code, despite not being something you would do in real
world scenarios, eventually ended up generating an exception for "Too
many open files".

  for ($i = 0; $i < 10000; $i++) {
    $client = new Predis\Client();
    $client->connect();
  }

The reason was that the destructor for the connection was not invoked
by PHP as expected when the client instance went out of scope, so the
underlying stream resource were not being properly released.
Even without an actual "connect()" operation, the memory usage of PHP
kept growing until exhausting the configured value of max memory.

The source of the issue is related to the use of closures as handlers
for the phpiredis reader resource, to be more precise it seems to be
some kind of circular reference memory leak. Apparently PHP does not
like the fact that a closure, automatically bound to "$this" starting
with PHP 5.4, is stored in the reader resource which in turn is kept
referenced by "$this". This ends up the refcount not going down thus
the GC does not collect those connection objects going out of scope.
This is confirmed by the fact that this issue is not triggered when
using PHP 5.3 where the ZE does not automatically bind closures to
"$this", unless you capture "$this" with the "use()" directive (and
the usual "$that = $this" trick).

Using a static assignement instead of simply returning new closures
in "getStatusHandler()" and "getErrorHandler()" is kind of an hack
that seems to be working fine, the added value of this solution is
that we do not have to break the internal API of the three connection
backends based on ext-phpiredis.

This commit fixes #345.
2016-06-14 21:27:42 +02:00
Daniele Alessandri e88ffc767d Merge remote-tracking branch 'github/pr/346' 2016-06-14 17:19:32 +02:00
Pascal Hofmann ce8b3fb683 Don't send AUTH and/or SELECT command after connecting to sentinels 2016-06-14 16:38:59 +02:00
Daniele Alessandri e0b4b2c20a [tests] Add test for Predis\Cluster\Hash\CRC16. 2016-06-13 18:58:02 +02:00
Daniele Alessandri 2640062803 Use master for connect() when sentinel reports no slaves.
Fixes #342.
2016-06-13 16:32:45 +02:00
orvice 1811288009 fix typos
possibile -> possible
indentify -> identify
publis ->  publish
2016-06-13 15:22:31 +02:00
Daniele Alessandri fa643ac20c Apply CS fixes. 2016-06-10 11:12:26 +02:00
Daniele Alessandri eb1e686ff9 [tests] Change indentation of mock method expectations. 2016-06-10 11:08:26 +02:00
Daniele Alessandri f02f3b6d37 [tests] Improve code coverage.
There is still some work to improve coverage in other classes though.
2016-06-09 16:41:19 +02:00
Daniele Alessandri d72a1b5550 Do not extend EVALSHA for ScriptCommand class. 2016-06-09 14:02:22 +02:00
Daniele Alessandri 8b466d05df Get string of basic connection parameters from parameters class. 2016-06-08 18:36:35 +02:00
Daniele Alessandri b553c6b9d0 Pass dispatcher loop instance to callback.
This should not break existing code but allows users to retrieve more
easily the current dispatcher loop instance without resorting to some
tricks (like relying on the "use()" directive with closures).
2016-06-08 12:12:31 +02:00
Daniele Alessandri c10479e238 Remove executeCommandOnNodes() from predis cluster backend.
It is still possible to achieve the same simply by iterating over the
connection or, even better, over the client instance.
2016-06-08 12:01:54 +02:00
Daniele Alessandri 228ccaebe3 Use static instead of self. 2016-06-07 21:01:59 +02:00
Daniele Alessandri 82c256422a Fix failure on PHP 5.3.
Also use get_called_class() where possible.
2016-06-07 20:42:08 +02:00
Daniele Alessandri ee7104d2e5 Replace "getClientFor()" with "on()" in Client.
This new method acts exactly like "getClientFor()" by returning a new
client instance for the specified node unless a callback is passed as
the second argument, in this case the callback is invoked and the new
client instance is passed to it. The value returned by the callback
is used as the return value of the "on()" method.
2016-06-07 20:00:11 +02:00
Daniele Alessandri 7c4c4ae58a Change methods of replication connection interface.
Promoted the "switchToMaster()" and "switchToSlave()" methods to be
part of the replication connection interface and demoted the method
"switchTo($connection)".
2016-06-07 18:50:11 +02:00
Daniele Alessandri 185e31e080 Make some fields of Predis\Client private. 2016-06-07 18:22:57 +02:00
Daniele Alessandri 64b3a4805c Apply CS fixes. 2016-06-07 15:23:32 +02:00
Daniele Alessandri 5b4e942112 Add notice about dangers of using master branch. 2016-06-07 15:23:32 +02:00
Daniele Alessandri b2547fc7f4 [tests] Ignore code coverage for Predis\Autoloader. 2016-06-07 15:23:32 +02:00
Daniele Alessandri d34bdd38c3 Reorganize and improve client options.
All option classes have been moved in the Predis\Configuration\Option
namespace and some have been optimized to have less impact on client
initialization timings.

Furthermore the accepted values for some options have been changed,
this is the complete list of accepted values:

- _aggregate_: callable returning an aggregate connection.
- cluster: string value ("predis", "redis"), callable returning an
  aggregate connection.
- replication: string value ("predis", "sentinel"), callable returning
  an aggregate connection.
- commands: command factory, named array mapping command IDs to PHP
  classes, callable returning a command factory or a named array.
- connections: connection factory, callable returning a connection
  factory, named array mapping connection schemes to PHP classes.
- _prefix_: string value, command processor, callable.
- _exceptions_: boolean value.

Note that the cluster and replication options now return a closure
acting as initializer instead of an aggregate connection.
2016-06-07 15:23:32 +02:00
Daniele Alessandri 1028599ac5 Split Predis\Connection\Aggregate namespace.
Now we have two namespaces for cluster and replication backends:

  - Predis\Connection\Cluster
  - Predis\Connection\Replication
2016-06-07 15:23:32 +02:00
Daniele Alessandri 822f02b8eb Implement new logic to load command classes.
By default Predis now uses a convention-over-configuration approach
by looking for a command class in the Predis\Command\Redis namespace
if it is not already defined in the commands class map.

This change allow us to decrease the time needed to load Predis on
each request since we removed 99% of the mappings in the commands
class map. Classes defined in the internal class map still take the
precedence over this mechanism, so users can still define their own
command classes to handle each command.
2016-06-07 15:23:28 +02:00
Daniele Alessandri 4b47639f9e Rename command classes using command ID as name.
Some notable exceptions are EVAL and ECHO because having these names
as class names would raise a syntax error.
2016-06-04 20:54:41 +02:00
Daniele Alessandri e4872af747 Modify ctor signature of Predis\Command\RawCommand. 2016-06-04 20:54:36 +02:00
Daniele Alessandri c1d34f022f Drop useless method from base command class. 2016-06-04 15:57:12 +02:00
Daniele Alessandri 62b421f20e Switch from server profiles to commands factory.
This change reduces some unnecessary complexity in the library, Redis
commands do not change much after all. Developers can still implement
their own commands factory, inject new commands or override existing
ones. The "profile" client options has been renamed to "commands" and
it accepts instances of Predis\Command\FactoryInterface.

The test suite checks at runtime the version of the running instance
of Redis for integration tests to adapt itself automatically.
2016-06-04 15:36:21 +02:00
Daniele Alessandri 6e3f301588 Move commands classes in Predis\Command\Redis. 2016-06-04 15:36:16 +02:00
Daniele Alessandri 3dccd6bf87 Back to development.
The master branch now hosts the development of Predis v2.0.0-dev.
2016-06-02 09:10:25 +02:00
549 changed files with 20545 additions and 17887 deletions
+1 -5
View File
@@ -1,13 +1,9 @@
* text=auto
/tests/PHPUnit export-ignore
/tests/Predis export-ignore
/tests/bootstrap.php export-ignore
/tests export-ignore
/.github export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.php_cs.dist export-ignore
/.travis.yml export-ignore
/phpunit.xml.dist export-ignore
/phpunit.xml.travisci export-ignore
+6
View File
@@ -0,0 +1,6 @@
The Predis code of conduct is derived from the Ruby code of conduct. Any violations of the code of conduct may be reported to [Till Krüss](https://till.im):
- Participants will be tolerant of opposing views.
- Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
- When interpreting the words and actions of others, participants should always assume good intentions.
- Behavior that can be reasonably considered harassment will not be tolerated.
+52
View File
@@ -0,0 +1,52 @@
name: Tests
on:
- push
- pull_request
jobs:
predis:
name: PHP ${{ matrix.php-versions }} (Redis ${{ matrix.redis-versions }})
runs-on: ubuntu-latest
services:
redis:
image: redis:${{ matrix.redis-versions }}
ports:
- 6379:6379
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
redis-versions: ['3', '4', '5', '6', '7']
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP with Composer and extensions
with:
php-version: ${{ matrix.php-versions }}
uses: shivammathur/setup-php@v2
- name: Get Composer cache directory
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache Composer dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
env:
PHP_VERSION: ${{ matrix.php-versions }}
run: composer install --no-progress --prefer-dist --optimize-autoloader $(if [ "$PHP_VERSION" == "8.0" ]; then echo "--ignore-platform-reqs"; fi;)
- name: Test with PHPUnit
run: vendor/bin/phpunit
+2 -6
View File
@@ -1,10 +1,6 @@
*.tgz
*.phar
/vendor
.php-version
.php_cs.cache
.phpunit.result.cache
phpunit.xml
package.xml
composer.lock
experiments/
vendor/
phpunit.xml
-35
View File
@@ -1,35 +0,0 @@
language: php
sudo: false
branches:
except:
- v0.5
- v0.6
- v0.6-PHP_5.2
- documentation
before_install:
- docker run -d --rm -p 127.0.0.1:6379:6379 redis:3
before_script:
- composer self-update
- composer install --no-interaction --prefer-source --dev
script:
- travis_retry vendor/bin/phpunit -c phpunit.xml.travisci
matrix:
fast_finish: true
include:
- php: 5.3
dist: precise
services: redis-server
before_install: skip
- php: 5.4
dist: trusty
- php: 5.5
dist: trusty
- php: 5.6
- php: 7.0
- php: 7.1
- php: 7.2
- php: 7.3
- php: 7.4
- php: nightly # PHP 8.0.0-dev
allow_failures:
- php: nightly
+146 -1041
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009-2016 Daniele Alessandri
Copyright (c) 2009-2021 Daniele Alessandri
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
+69 -60
View File
@@ -4,20 +4,16 @@
[![Latest stable][ico-version-stable]][link-packagist]
[![Latest development][ico-version-dev]][link-packagist]
[![Monthly installs][ico-downloads-monthly]][link-downloads]
[![Build status][ico-travis]][link-travis]
[![Build status][ico-build]][link-actions]
Flexible and feature-complete [Redis](http://redis.io) client for PHP >= 5.3 and HHVM >= 2.3.0.
Predis does not require any additional C extension by default, but it can be optionally paired with
[phpiredis](https://github.com/nrk/phpiredis) to lower the overhead of the serialization and parsing
of the [Redis RESP Protocol](http://redis.io/topics/protocol).
A flexible and feature-complete [Redis](http://redis.io) client for PHP 7.2 and newer.
More details about this project can be found on the [frequently asked questions](FAQ.md).
## Main features ##
- Support for different versions of Redis (from __2.0__ to __3.2__) using profiles.
- Support for Redis from __3.0__ to __7.0__.
- Support for clustering using client-side sharding and pluggable keyspace distributors.
- Support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0).
- Support for master-slave replication setups and [redis-sentinel](http://redis.io/topics/sentinel).
@@ -30,15 +26,18 @@ More details about this project can be found on the [frequently asked questions]
- Connections can be established via TCP/IP (also TLS/SSL-encrypted) or UNIX domain sockets.
- Support for [Webdis](http://webd.is) (requires both `ext-curl` and `ext-phpiredis`).
- Support for custom connection classes for providing different network or protocol backends.
- Flexible system for defining custom commands and profiles and override the default ones.
- Flexible system for defining custom commands and override the default ones.
## How to _install_ and use Predis ##
This library can be found on [Packagist](http://packagist.org/packages/predis/predis) for an easier
management of projects dependencies using [Composer](http://packagist.org/about-composer) or on our
[own PEAR channel](http://pear.nrk.io) for a more traditional installation using PEAR. Ultimately,
compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases).
management of projects dependencies using [Composer](http://packagist.org/about-composer).
Compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases).
```shell
composer require predis/predis
```
### Loading the library ###
@@ -55,10 +54,6 @@ require 'Predis/Autoloader.php';
Predis\Autoloader::register();
```
It is also possible to create a [phar](http://www.php.net/manual/en/intro.phar.php) archive directly
from the repository by launching the `bin/create-phar` script. The generated phar already contains a
stub defining its own autoloader, so you just need to `require()` it to start using the library.
### Connecting to Redis ###
@@ -122,14 +117,16 @@ the rules described on their respective IANA provisional registration documents.
The actual list of supported connection parameters can vary depending on each connection backend so
it is recommended to refer to their specific documentation or implementation for details.
When an array of connection parameters is provided, Predis automatically works in cluster mode using
client-side sharding. Both named arrays and URI strings can be mixed when providing configurations
Predis can aggregate multiple connections when providing an array of connection parameters and the
appropriate option to instruct the client about how to aggregate them (clustering, replication or a
custom aggregation logic). Named arrays and URI strings can be mixed when providing configurations
for each node:
```php
$client = new Predis\Client([
'tcp://10.0.0.1?alias=first-node',
['host' => '10.0.0.2', 'alias' => 'second-node'],
'tcp://10.0.0.1?alias=first-node', ['host' => '10.0.0.2', 'alias' => 'second-node'],
], [
'cluster' => 'predis',
]);
```
@@ -148,20 +145,20 @@ Many aspects and behaviors of the client can be configured by passing specific c
second argument of `Predis\Client::__construct()`:
```php
$client = new Predis\Client($parameters, ['profile' => '2.8', 'prefix' => 'sample:']);
$client = new Predis\Client($parameters, ['prefix' => 'sample:']);
```
Options are managed using a mini DI-alike container and their values can be lazily initialized only
when needed. The client options supported by default in Predis are:
- `profile`: specifies the profile to use to match a specific version of Redis.
- `prefix`: prefix string automatically applied to keys found in commands.
- `prefix`: prefix string applied to every key found in commands.
- `exceptions`: whether the client should throw or return responses upon Redis errors.
- `connections`: list of connection backends or a connection factory instance.
- `cluster`: specifies a cluster backend (`predis`, `redis` or callable object).
- `replication`: specifies a replication backend (`TRUE`, `sentinel` or callable object).
- `aggregate`: overrides `cluster` and `replication` to provide a custom connections aggregator.
- `cluster`: specifies a cluster backend (`predis`, `redis` or callable).
- `replication`: specifies a replication backend (`predis`, `sentinel` or callable).
- `aggregate`: configures the client with a custom aggregate connection (callable).
- `parameters`: list of default connection parameters for aggregate connections.
- `commands`: specifies a command factory instance to use through the library.
Users can also provide custom options with values or callable objects (for lazy initialization) that
are stored in the options container for later use through the library.
@@ -172,18 +169,18 @@ are stored in the options container for later use through the library.
Aggregate connections are the foundation upon which Predis implements clustering and replication and
they are used to group multiple connections to single Redis nodes and hide the specific logic needed
to handle them properly depending on the context. Aggregate connections usually require an array of
connection parameters when creating a new client instance.
connection parameters along with the appropriate client option when creating a new client instance.
#### Cluster ####
By default, when no specific client options are set and an array of connection parameters is passed
to the client's constructor, Predis configures itself to work in clustering mode using a traditional
client-side sharding approach to create a cluster of independent nodes and distribute the keyspace
among them. This approach needs some form of external health monitoring of nodes and requires manual
operations to rebalance the keyspace when changing its configuration by adding or removing nodes:
Predis can be configured to work in clustering mode with a traditional client-side sharding approach
to create a cluster of independent nodes and distribute the keyspace among them. This approach needs
some sort of external health monitoring of nodes and requires the keyspace to be rebalanced manually
when nodes are added or removed:
```php
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['cluster' => 'predis'];
$client = new Predis\Client($parameters);
```
@@ -213,12 +210,12 @@ the value of a key. Instead of raising a connection error when a slave fails, th
fall back to a different slave among the ones provided in the configuration.
The basic configuration needed to use the client in replication mode requires one Redis server to be
identified as the master (this can be done via connection parameters using the `alias` parameter set
to `master`) and one or more servers acting as slaves:
identified as the master (this can be done via connection parameters by setting the `role` parameter
to `master`) and one or more slaves (in this case setting `role` to `slave` for slaves is optional):
```php
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => true];
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => 'predis'];
$client = new Predis\Client($parameters, $options);
```
@@ -259,13 +256,13 @@ when certain Lua scripts do not perform write operations it is possible to provi
the client to stick with slaves for their execution:
```php
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => function () {
// Set scripts that won't trigger a switch from a slave to the master node.
$strategy = new Predis\Replication\ReplicationStrategy();
$strategy->setScriptReadOnly($LUA_SCRIPT);
return new Predis\Connection\Aggregate\MasterSlaveReplication($strategy);
return new Predis\Connection\Replication\MasterSlaveReplication($strategy);
}];
$client = new Predis\Client($parameters, $options);
@@ -324,7 +321,7 @@ of a transaction using CAS you can see [the following example](examples/transact
While we try to update Predis to stay up to date with all the commands available in Redis, you might
prefer to stick with an old version of the library or provide a different way to filter arguments or
parse responses for specific commands. To achieve that, Predis provides the ability to implement new
command classes to define or override commands in the default server profiles used by the client:
command classes to define or override commands in the default command factory used by the client:
```php
// Define a new command by extending Predis\Command\Command:
@@ -336,9 +333,12 @@ class BrandNewRedisCommand extends Predis\Command\Command
}
}
// Inject your command in the current profile:
$client = new Predis\Client();
$client->getProfile()->defineCommand('newcmd', 'BrandNewRedisCommand');
// Inject your command in the current command factory:
$client = new Predis\Client($parameters, [
'commands' => [
'newcmd' => 'BrandNewRedisCommand',
],
]);
$response = $client->newcmd();
```
@@ -357,7 +357,7 @@ $response = $client->executeRaw(['SET', 'foo', 'bar']);
While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using
directly [`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha),
Predis offers script commands as an higher level abstraction built upon them to make things simple.
Script commands can be registered in the server profile used by the client and are accessible as if
Script commands can be registered in the command factory used by the client and are accessible as if
they were plain Redis commands, but they define Lua scripts that get transmitted to the server for
remote execution. Internally they use [`EVALSHA`](http://redis.io/commands/evalsha) by default and
identify a script by its SHA1 hash to save bandwidth, but [`EVAL`](http://redis.io/commands/eval)
@@ -383,9 +383,12 @@ LUA;
}
}
// Inject the script command in the current profile:
$client = new Predis\Client();
$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue');
// Inject the script command in the current command factory:
$client = new Predis\Client($parameters, [
'commands' => [
'lpushrand' => 'ListPushRandomValue',
],
]);
$response = $client->lpushrand('random_values', $seed = mt_rand());
```
@@ -407,6 +410,17 @@ $client = new Predis\Client('tcp://127.0.0.1', [
]);
```
The client can also be configured to rely on a [phpiredis](https://github.com/nrk/phpiredis)-backend
by specifying a descriptive string for the `connections` client option. Supported string values are:
- `phpiredis-stream` maps `tcp`, `redis` and `unix` to `Predis\Connection\PhpiredisStreamConnection`
- `phpiredis-socket` maps `tcp`, `redis` and `unix` to `Predis\Connection\PhpiredisSocketConnection`
- `phpiredis` is simply an alias of `phpiredis-stream`
```php
$client = new Predis\Client('tcp://127.0.0.1', ['connections' => 'phpiredis']);
```
Developers can create their own connection classes to support whole new network backends, extend
existing classes or provide completely different implementations. Connection classes must implement
`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
@@ -443,18 +457,14 @@ stay consistent while working on the project.
__ATTENTION__: Do not ever run the test suite shipped with Predis against instances of Redis running
in production environments or containing data you are interested in!
Predis has a comprehensive test suite covering every aspect of the library. This test suite performs
integration tests against a running instance of Redis (>= 2.4.0 is required) to verify the correct
behavior of the implementation of each command and automatically skips commands not defined in the
specified Redis profile. If you do not have Redis up and running, integration tests can be disabled.
By default the test suite is configured to execute integration tests using the profile for Redis 3.2
(which is the current stable version of Redis) but can optionally target a Redis instance built from
the `unstable` branch by modifying `phpunit.xml` and setting `REDIS_SERVER_VERSION` to `dev` so that
the development server profile will be used. You can refer to [the tests README](tests/README.md)
for more detailed information about testing Predis.
Predis has a comprehensive test suite covering every aspect of the library and that can optionally
perform integration tests against a running instance of Redis (required >= 2.4.0 in order to verify
the correct behavior of the implementation of each command. Integration tests for unsupported Redis
commands are automatically skipped. If you do not have Redis up and running, integration tests can
be disabled. See [the tests README](tests/README.md) for more details about testing this library.
Predis uses Travis CI for continuous integration and the history for past and current builds can be
found [on its project page](http://travis-ci.org/predis/predis).
Predis uses GitHub Actions for continuous integration and the history for past and current builds can be
found [on its actions page](https://github.com/predis/predis/actions).
## Other ##
@@ -465,12 +475,12 @@ found [on its project page](http://travis-ci.org/predis/predis).
- [Source code](https://github.com/predis/predis)
- [Wiki](https://github.com/predis/predis/wiki)
- [Issue tracker](https://github.com/predis/predis/issues)
- [PEAR channel](http://pear.nrk.io)
### Author ###
- [Daniele Alessandri](mailto:suppakilla@gmail.com) ([twitter](http://twitter.com/JoL1hAHN))
- [Till Krüss](https://till.im) ([Twitter](http://twitter.com/tillkruss))
### License ###
@@ -481,9 +491,8 @@ The code for Predis is distributed under the terms of the MIT license (see [LICE
[ico-version-stable]: https://img.shields.io/packagist/v/predis/predis.svg?style=flat-square
[ico-version-dev]: https://img.shields.io/packagist/vpre/predis/predis.svg?style=flat-square
[ico-downloads-monthly]: https://img.shields.io/packagist/dm/predis/predis.svg?style=flat-square
[ico-travis]: https://img.shields.io/travis/predis/predis.svg?style=flat-square
[ico-hhvm]: https://img.shields.io/hhvm/predis/predis.svg?style=flat-square
[ico-build]: https://img.shields.io/github/workflow/status/predis/predis/Tests/main?style=flat-square
[link-packagist]: https://packagist.org/packages/predis/predis
[link-travis]: https://travis-ci.org/predis/predis
[link-actions]: https://github.com/predis/predis/actions
[link-downloads]: https://packagist.org/packages/predis/predis/stats
+1 -1
View File
@@ -1 +1 @@
1.1.4
2.0.3
+24 -21
View File
@@ -15,7 +15,7 @@
// of a test case to test a Redis command by specifying the name of the class
// in the Predis\Command namespace (only classes in this namespace are valid).
// For example, to generate a test case for SET (which is represented by the
// Predis\Command\StringSet class):
// Predis\Command\Redis\StringSet class):
//
// $ ./bin/generate-command-test --class=StringSet
//
@@ -45,6 +45,11 @@ class CommandTestCaseGenerator
if (!isset($options['class'])) {
throw new RuntimeException("Missing 'class' option.");
}
if (!isset($options['realm'])) {
throw new RuntimeException("Missing 'realm' option.");
}
$this->options = $options;
}
@@ -92,8 +97,12 @@ class CommandTestCaseGenerator
throw new RuntimeException("Missing 'class' option.");
}
$options['fqn'] = "Predis\\Command\\{$options['class']}";
$options['path'] = "Command/{$options['class']}.php";
if (!isset($options['realm'])) {
throw new RuntimeException("Missing 'realm' option.");
}
$options['fqn'] = "Predis\\Command\\Redis\\{$options['class']}";
$options['path'] = "Command/Redis/{$options['class']}.php";
$source = __DIR__.'/../src/'.$options['path'];
if (!file_exists($source)) {
@@ -101,7 +110,7 @@ class CommandTestCaseGenerator
}
if (!isset($options['output'])) {
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', 'Test.php', $options['path']));
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', '_Test.php', $options['path']));
}
return new self($options);
@@ -109,18 +118,11 @@ class CommandTestCaseGenerator
protected function getTestRealm()
{
if (isset($this->options['realm'])) {
if (!$this->options['realm']) {
throw new RuntimeException('Invalid value for realm has been sepcified (empty).');
}
return $this->options['realm'];
if (empty($this->options['realm'])) {
throw new RuntimeException('Invalid value for realm has been sepcified (empty).');
}
$fqnParts = explode('\\', $this->options['fqn']);
$class = array_pop($fqnParts);
list($realm,) = preg_split('/([[:upper:]][[:lower:]]+)/', $class, 2, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
return strtolower($realm);
return $this->options['realm'];
}
public function generate()
@@ -130,6 +132,7 @@ class CommandTestCaseGenerator
if (!$reflection->isInstantiable()) {
throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed.");
}
if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) {
throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface.");
}
@@ -173,7 +176,7 @@ class CommandTestCaseGenerator
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
/**
* @group commands
@@ -184,7 +187,7 @@ class $class extends PredisCommandTestCase
/**
* {@inheritdoc}
*/
protected function getExpectedCommand()
protected function getExpectedCommand(): string
{
return '$fqn';
}
@@ -192,7 +195,7 @@ class $class extends PredisCommandTestCase
/**
* {@inheritdoc}
*/
protected function getExpectedId()
protected function getExpectedId(): string
{
return '$id';
}
@@ -200,7 +203,7 @@ class $class extends PredisCommandTestCase
/**
* @group disconnected
*/
public function testFilterArguments()
public function testFilterArguments(): void
{
\$this->markTestIncomplete('This test has not been implemented yet.');
@@ -216,7 +219,7 @@ class $class extends PredisCommandTestCase
/**
* @group disconnected
*/
public function testParseResponse()
public function testParseResponse(): void
{
\$this->markTestIncomplete('This test has not been implemented yet.');
@@ -236,7 +239,7 @@ PHP;
/**
* @group disconnected
*/
public function testPrefixKeys()
public function testPrefixKeys(): void
{
\$this->markTestIncomplete('This test has not been implemented yet.');
@@ -252,7 +255,7 @@ PHP;
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
public function testPrefixKeysIgnoredOnEmptyArguments(): void
{
\$command = \$this->getCommand();
\$command->prefixKeys('prefix:');
-233
View File
@@ -1,233 +0,0 @@
#!/usr/bin/env php
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// -------------------------------------------------------------------------- //
// In order to be able to execute this script to create a PEAR package of Predis
// the `pear` binary must be available and executable in your $PATH.
// The parts used to parse author and version strings are taken from Onion (used
// by this library in the past) just to keep on relying on the package.ini file
// to simplify things. We might consider to switch to using the PEAR classes
// directly in the future.
// -------------------------------------------------------------------------- //
function executeWithBackup($file, $callback)
{
$exception = null;
$backup = "$file.backup";
copy($file, $backup);
try {
call_user_func($callback, $file);
} catch (Exception $exception) {
// NOOP
}
unlink($file);
rename($backup, $file);
if ($exception) {
throw $exception;
}
}
function parseAuthor($string)
{
$author = array();
if (preg_match('/^\s*(.+?)\s*(?:"(\S+)"\s*)?<(\S+)>\s*$/x', $string , $regs)) {
if (count($regs) == 4) {
list($_,$name,$user,$email) = $regs;
$author['name'] = $name;
$author['user'] = $user;
$author['email'] = $email;
} elseif (count($regs) == 3) {
list($_,$name,$email) = $regs;
$author['name'] = $name;
$author['email'] = $email;
}
} else {
$author['name'] = $string;
}
return $author;
}
function parseVersion($string)
{
$version_pattern = '([0-9.]+)';
if (preg_match("/^\s*$version_pattern\s*\$/x", $string, $regs)) {
return array('min' => $regs[1] ?: '0.0.0');
} elseif (preg_match("/^\s*[>=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
return array('min' => $regs[1] ?: '0.0.0');
} elseif (preg_match("/^\s*[<=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
return array('max' => $regs[1]);
} elseif (preg_match("/^\s*$version_pattern\s*<=>\s*$version_pattern\s*\$/x", $string, $regs)) {
return array(
'min' => $regs[1] ?: '0.0.0',
'max' => $regs[2],
);
}
return null;
}
function addRolePath($pkg, $path, $role)
{
if (is_dir($path)) {
$dirRoot = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$dirTree = new RecursiveIteratorIterator($dirRoot, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($dirTree as $fileinfo) {
if ($fileinfo->isFile()) {
addPackageFile($pkg, $fileinfo, $role, $path);
}
}
} else {
foreach (glob($path) as $filename) {
addPackageFile($pkg, new SplFileInfo($filename), $role);
}
}
}
function addPackageFile($pkg, $fileinfo, $role, $baseDir = '')
{
$fileNode = $pkg->contents->dir->addChild('file');
$fileNode->addAttribute('name', $filepath = $fileinfo->getPathname());
$fileNode->addAttribute('role', $role);
$fileNode->addAttribute('md5sum', md5_file($filepath));
$installNode = $pkg->phprelease->filelist->addChild('install');
$installNode->addAttribute('name', $filepath);
$installNode->addAttribute('as', !$baseDir ? basename($filepath) : substr($filepath, strlen($baseDir) + 1));
}
function generatePackageXml($packageINI)
{
$XML = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.4.10" version="2.0"
xmlns="http://pear.php.net/dtd/package-2.0"
xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd" />
XML;
$cfg = parse_ini_file($packageINI, true);
$pkg = new SimpleXMLElement($XML);
$pkg->name = $cfg['package']['name'];
$pkg->channel = $cfg['package']['channel'];
$pkg->summary = $cfg['package']['desc'];
$pkg->description = $cfg['package']['desc'];
$author = parseAuthor($cfg['package']['author']);
$pkg->addChild('lead');
$pkg->lead->name = $author['name'];
$pkg->lead->user = $author['user'];
$pkg->lead->email = $author['email'];
$pkg->lead->active = 'yes';
$datetime = new DateTime('now');
$pkg->date = $datetime->format('Y-m-d');
$pkg->time = $datetime->format('H:i:s');
$pkg->addChild('version');
$pkg->version->release = $cfg['package']['version'];
$pkg->version->api = $cfg['package']['version'];
$pkg->addChild('stability');
$pkg->stability->release = $cfg['package']['stability'];
$pkg->stability->api = $cfg['package']['stability'];
$pkg->license = $cfg['package']['license'];
$pkg->notes = '-';
$pkg->addChild('contents')->addChild('dir')->addAttribute('name', '/');
$pkg->addChild('dependencies')->addChild('required');
foreach ($cfg['require'] as $required => $version) {
$version = parseVersion($version);
$pkg->dependencies->required->addChild($required);
if (isset($version['min'])) {
$pkg->dependencies->required->$required->min = $version['min'];
}
if (isset($version['max'])) {
$pkg->dependencies->required->$required->min = $version['max'];
}
}
$pkg->addChild('phprelease')->addChild('filelist');
$pathToRole = array(
'doc' => 'doc', 'docs' => 'doc', 'examples' => 'doc',
'lib' => 'php', 'src' => 'php',
'test' => 'test', 'tests' => 'test',
);
foreach (array_merge($pathToRole, $cfg['roles'] ?: array()) as $path => $role) {
addRolePath($pkg, $path, $role);
}
return $pkg;
}
function rewritePackageInstallAs($pkg)
{
foreach ($pkg->phprelease->filelist->install as $file) {
if (preg_match('/^src\//', $file['name'])) {
$file['as'] = "Predis/{$file['as']}";
}
}
}
function savePackageXml($xml)
{
$dom = new DOMDocument("1.0", "UTF-8");
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
file_put_contents('package.xml', $dom->saveXML());
}
function buildPackage()
{
passthru('pear -q package && rm package.xml');
}
function modifyPhpunitXml($file)
{
$cfg = new SimpleXMLElement($file, null, true);
$cfg[0]['bootstrap'] = str_replace('tests/', '', $cfg[0]['bootstrap']);
$cfg->testsuites->testsuite->directory = str_replace('tests/', '', $cfg->testsuites->testsuite->directory);
$cfg->saveXml($file);
}
// -------------------------------------------------------------------------- //
executeWithBackup(__DIR__.'/../phpunit.xml.dist', function ($file) {
modifyPhpunitXml($file);
$pkg = generatePackageXml('package.ini');
rewritePackageInstallAs($pkg);
savePackageXml($pkg);
buildPackage();
});
-71
View File
@@ -1,71 +0,0 @@
#!/usr/bin/env php
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// -------------------------------------------------------------------------- //
// In order to be able to execute this script to create a Phar archive of Predis,
// the Phar module must be loaded and the "phar.readonly" directive php.ini must
// be set to "off". You can change the values in the $options array to customize
// the creation of the Phar archive to better suit your needs.
// -------------------------------------------------------------------------- //
$options = array(
'name' => 'predis',
'project_path' => __DIR__ . '/../src',
'compression' => Phar::NONE,
'append_version' => true,
);
function getPharFilename($options)
{
$filename = $options['name'];
// NOTE: do not consider "append_version" with Phar compression do to a bug in
// Phar::compress() when renaming phar archives containing dots in their name.
if ($options['append_version'] && $options['compression'] === Phar::NONE) {
$versionFile = @fopen(__DIR__ . '/../VERSION', 'r');
if ($versionFile === false) {
throw new Exception("Could not locate the VERSION file.");
}
$version = trim(fgets($versionFile));
fclose($versionFile);
$filename .= "_$version";
}
return "$filename.phar";
}
function getPharStub($options)
{
return <<<EOSTUB
<?php
Phar::mapPhar('predis.phar');
spl_autoload_register(function (\$class) {
if (strpos(\$class, 'Predis\\\\') === 0) {
\$file = 'phar://predis.phar/'.strtr(substr(\$class, 7), '\\\', '/').'.php';
if (file_exists(\$file)) {
require \$file;
return true;
}
}
});
__halt_compiler();
EOSTUB;
}
// -------------------------------------------------------------------------- //
$phar = new Phar(getPharFilename($options));
$phar->compress($options['compression']);
$phar->setStub(getPharStub($options));
$phar->buildFromDirectory($options['project_path']);
-662
View File
@@ -1,662 +0,0 @@
#!/usr/bin/env php
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// -------------------------------------------------------------------------- //
// This script can be used to automatically glue all the .php files of Predis
// into a single monolithic script file that can be used without an autoloader,
// just like the other previous versions of the library.
//
// Much of its complexity is due to the fact that we cannot simply join PHP
// files, but namespaces and classes definitions must follow a precise order
// when dealing with subclassing and inheritance.
//
// The current implementation is pretty naïve, but it should do for now.
// -------------------------------------------------------------------------- //
class CommandLine
{
public static function getOptions()
{
$parameters = array(
's:' => 'source:',
'o:' => 'output:',
'e:' => 'exclude:',
'E:' => 'exclude-classes:',
);
$getops = getopt(implode(array_keys($parameters)), $parameters);
$options = array(
'source' => __DIR__ . "/../src",
'output' => PredisFile::NS_ROOT . '.php',
'exclude' => array(),
);
foreach ($getops as $option => $value) {
switch ($option) {
case 's':
case 'source':
$options['source'] = $value;
break;
case 'o':
case 'output':
$options['output'] = $value;
break;
case 'E':
case 'exclude-classes':
$options['exclude'] = @file($value, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: $value;
break;
case 'e':
case 'exclude':
$options['exclude'] = is_array($value) ? $value : array($value);
break;
}
}
return $options;
}
}
class PredisFile
{
const NS_ROOT = 'Predis';
private $namespaces;
public function __construct()
{
$this->namespaces = array();
}
public static function from($libraryPath, array $exclude = array())
{
$predisFile = new PredisFile();
$libIterator = new RecursiveDirectoryIterator($libraryPath);
foreach (new RecursiveIteratorIterator($libIterator) as $classFile)
{
if (!$classFile->isFile()) {
continue;
}
$namespace = self::NS_ROOT.strtr(str_replace($libraryPath, '', $classFile->getPath()), '/', '\\');
if (in_array(sprintf('%s\\%s', $namespace, $classFile->getBasename('.php')), $exclude)) {
continue;
}
$phpNamespace = $predisFile->getNamespace($namespace);
if ($phpNamespace === false) {
$phpNamespace = new PhpNamespace($namespace);
$predisFile->addNamespace($phpNamespace);
}
$phpClass = new PhpClass($phpNamespace, $classFile);
}
return $predisFile;
}
public function addNamespace(PhpNamespace $namespace)
{
if (isset($this->namespaces[(string)$namespace])) {
throw new InvalidArgumentException("Duplicated namespace");
}
$this->namespaces[(string)$namespace] = $namespace;
}
public function getNamespaces()
{
return $this->namespaces;
}
public function getNamespace($namespace)
{
if (!isset($this->namespaces[$namespace])) {
return false;
}
return $this->namespaces[$namespace];
}
public function getClassByFQN($classFqn)
{
if (($nsLastPos = strrpos($classFqn, '\\')) !== false) {
$namespace = $this->getNamespace(substr($classFqn, 0, $nsLastPos));
if ($namespace === false) {
return null;
}
$className = substr($classFqn, $nsLastPos + 1);
return $namespace->getClass($className);
}
return null;
}
private function calculateDependencyScores(&$classes, $fqn)
{
if (!isset($classes[$fqn])) {
$classes[$fqn] = 0;
}
$classes[$fqn] += 1;
if (($phpClass = $this->getClassByFQN($fqn)) === null) {
throw new RuntimeException(
"Cannot found the class $fqn which is required by other subclasses. Are you missing a file?"
);
}
foreach ($phpClass->getDependencies() as $fqn) {
$this->calculateDependencyScores($classes, $fqn);
}
}
private function getDependencyScores()
{
$classes = array();
foreach ($this->getNamespaces() as $phpNamespace) {
foreach ($phpNamespace->getClasses() as $phpClass) {
$this->calculateDependencyScores($classes, $phpClass->getFQN());
}
}
return $classes;
}
private function getOrderedNamespaces($dependencyScores)
{
$namespaces = array_fill_keys(array_unique(
array_map(
function ($fqn) { return PhpNamespace::extractName($fqn); },
array_keys($dependencyScores)
)
), 0);
foreach ($dependencyScores as $classFqn => $score) {
$namespaces[PhpNamespace::extractName($classFqn)] += $score;
}
arsort($namespaces);
return array_keys($namespaces);
}
private function getOrderedClasses(PhpNamespace $phpNamespace, $classes)
{
$nsClassesFQNs = array_map(function ($cl) { return $cl->getFQN(); }, $phpNamespace->getClasses());
$nsOrderedClasses = array();
foreach ($nsClassesFQNs as $nsClassFQN) {
$nsOrderedClasses[$nsClassFQN] = $classes[$nsClassFQN];
}
arsort($nsOrderedClasses);
return array_keys($nsOrderedClasses);
}
public function getPhpCode()
{
$buffer = array("<?php\n\n", PhpClass::LICENSE_HEADER, "\n\n");
$classes = $this->getDependencyScores();
$namespaces = $this->getOrderedNamespaces($classes);
foreach ($namespaces as $namespace) {
$phpNamespace = $this->getNamespace($namespace);
// generate namespace directive
$buffer[] = $phpNamespace->getPhpCode();
$buffer[] = "\n";
// generate use directives
$useDirectives = $phpNamespace->getUseDirectives();
if (count($useDirectives) > 0) {
$buffer[] = $useDirectives->getPhpCode();
$buffer[] = "\n";
}
// generate classes bodies
$nsClasses = $this->getOrderedClasses($phpNamespace, $classes);
foreach ($nsClasses as $classFQN) {
$buffer[] = $this->getClassByFQN($classFQN)->getPhpCode();
$buffer[] = "\n\n";
}
$buffer[] = "/* " . str_repeat("-", 75) . " */";
$buffer[] = "\n\n";
}
return implode($buffer);
}
public function saveTo($outputFile)
{
// TODO: add more sanity checks
if ($outputFile === null || $outputFile === '') {
throw new InvalidArgumentException('You must specify a valid output file');
}
file_put_contents($outputFile, $this->getPhpCode());
}
}
class PhpNamespace implements IteratorAggregate
{
private $namespace;
private $classes;
public function __construct($namespace)
{
$this->namespace = $namespace;
$this->classes = array();
$this->useDirectives = new PhpUseDirectives($this);
}
public static function extractName($fqn)
{
$nsSepLast = strrpos($fqn, '\\');
if ($nsSepLast === false) {
return $fqn;
}
$ns = substr($fqn, 0, $nsSepLast);
return $ns !== '' ? $ns : null;
}
public function addClass(PhpClass $class)
{
$this->classes[$class->getName()] = $class;
}
public function getClass($className)
{
if (isset($this->classes[$className])) {
return $this->classes[$className];
}
}
public function getClasses()
{
return array_values($this->classes);
}
public function getIterator()
{
return new \ArrayIterator($this->getClasses());
}
public function getUseDirectives()
{
return $this->useDirectives;
}
public function getPhpCode()
{
return "namespace $this->namespace;\n";
}
public function __toString()
{
return $this->namespace;
}
}
class PhpUseDirectives implements Countable, IteratorAggregate
{
private $use;
private $aliases;
private $reverseAliases;
private $namespace;
public function __construct(PhpNamespace $namespace)
{
$this->namespace = $namespace;
$this->use = array();
$this->aliases = array();
$this->reverseAliases = array();
}
public function add($use, $as = null)
{
if (in_array($use, $this->use)) {
return;
}
$rename = null;
$this->use[] = $use;
$aliasedClassName = $as ?: PhpClass::extractName($use);
if (isset($this->aliases[$aliasedClassName])) {
$parentNs = $this->getParentNamespace();
if ($parentNs && false !== $pos = strrpos($parentNs, '\\')) {
$parentNs = substr($parentNs, $pos);
}
$newAlias = "{$parentNs}_{$aliasedClassName}";
$rename = (object) array(
'namespace' => $this->namespace,
'from' => $aliasedClassName,
'to' => $newAlias,
);
$this->aliases[$newAlias] = $use;
$as = $newAlias;
} else {
$this->aliases[$aliasedClassName] = $use;
}
if ($as !== null) {
$this->reverseAliases[$use] = $as;
}
return $rename;
}
public function getList()
{
return $this->use;
}
public function getIterator()
{
return new \ArrayIterator($this->getList());
}
public function getPhpCode()
{
$reverseAliases = $this->reverseAliases;
$reducer = function ($str, $use) use ($reverseAliases) {
if (isset($reverseAliases[$use])) {
return $str .= "use $use as {$reverseAliases[$use]};\n";
} else {
return $str .= "use $use;\n";
}
};
return array_reduce($this->getList(), $reducer, '');
}
public function getNamespace()
{
return $this->namespace;
}
public function getParentNamespace()
{
if (false !== $pos = strrpos($this->namespace, '\\')) {
return substr($this->namespace, 0, $pos);
}
return '';
}
public function getFQN($className)
{
if (($nsSepFirst = strpos($className, '\\')) === false) {
if (isset($this->aliases[$className])) {
return $this->aliases[$className];
}
return (string)$this->getNamespace() . "\\$className";
}
if ($nsSepFirst != 0) {
throw new InvalidArgumentException("Partially qualified names are not supported");
}
return $className;
}
public function count()
{
return count($this->use);
}
}
class PhpClass
{
const LICENSE_HEADER = <<<LICENSE
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
LICENSE;
private $namespace;
private $file;
private $body;
private $implements;
private $extends;
private $name;
public function __construct(PhpNamespace $namespace, SplFileInfo $classFile)
{
$this->namespace = $namespace;
$this->file = $classFile;
$this->implements = array();
$this->extends = array();
$this->extractData();
$namespace->addClass($this);
}
public static function extractName($fqn)
{
$nsSepLast = strrpos($fqn, '\\');
if ($nsSepLast === false) {
return $fqn;
}
return substr($fqn, $nsSepLast + 1);
}
private function extractData()
{
$renames = array();
$useDirectives = $this->getNamespace()->getUseDirectives();
$useExtractor = function ($m) use ($useDirectives, &$renames) {
array_shift($m);
if (isset($m[1])) {
$m[1] = str_replace(" as ", '', $m[1]);
}
if ($rename = call_user_func_array(array($useDirectives, 'add'), $m)) {
$renames[] = $rename;
}
};
$classBuffer = stream_get_contents(fopen($this->getFile()->getPathname(), 'r'));
$classBuffer = str_replace(self::LICENSE_HEADER, '', $classBuffer);
$classBuffer = preg_replace('/<\?php\s?\\n\s?/', '', $classBuffer);
$classBuffer = preg_replace('/\s?\?>\n?/ms', '', $classBuffer);
$classBuffer = preg_replace('/namespace\s+[\w\d_\\\\]+;\s?/', '', $classBuffer);
$classBuffer = preg_replace_callback('/use\s+([\w\d_\\\\]+)(\s+as\s+.*)?;\s?\n?/', $useExtractor, $classBuffer);
foreach ($renames as $rename) {
$classBuffer = str_replace($rename->from, $rename->to, $classBuffer);
}
$this->body = trim($classBuffer);
$this->extractHierarchy();
}
private function extractHierarchy()
{
$implements = array();
$extends = array();
$extractor = function ($iterator, $callback) {
$className = '';
$iterator->seek($iterator->key() + 1);
while ($iterator->valid()) {
$token = $iterator->current();
if (is_string($token)) {
if (preg_match('/\s?,\s?/', $token)) {
$callback(trim($className));
$className = '';
} else if ($token == '{') {
$callback(trim($className));
return;
}
}
switch ($token[0]) {
case T_NS_SEPARATOR:
$className .= '\\';
break;
case T_STRING:
$className .= $token[1];
break;
case T_IMPLEMENTS:
case T_EXTENDS:
$callback(trim($className));
$iterator->seek($iterator->key() - 1);
return;
}
$iterator->next();
}
};
$tokens = token_get_all("<?php\n" . trim($this->getPhpCode()));
$iterator = new ArrayIterator($tokens);
while ($iterator->valid()) {
$token = $iterator->current();
if (is_string($token)) {
$iterator->next();
continue;
}
switch ($token[0]) {
case T_CLASS:
case T_INTERFACE:
$iterator->seek($iterator->key() + 2);
$tk = $iterator->current();
$this->name = $tk[1];
break;
case T_IMPLEMENTS:
$extractor($iterator, function ($fqn) use (&$implements) {
$implements[] = $fqn;
});
break;
case T_EXTENDS:
$extractor($iterator, function ($fqn) use (&$extends) {
$extends[] = $fqn;
});
break;
}
$iterator->next();
}
$this->implements = $this->guessFQN($implements);
$this->extends = $this->guessFQN($extends);
}
public function guessFQN($classes)
{
$useDirectives = $this->getNamespace()->getUseDirectives();
return array_map(array($useDirectives, 'getFQN'), $classes);
}
public function getImplementedInterfaces($all = false)
{
if ($all) {
return $this->implements;
}
return array_filter(
$this->implements,
function ($cn) { return strpos($cn, 'Predis\\') === 0; }
);
}
public function getExtendedClasses($all = false)
{
if ($all) {
return $this->extemds;
}
return array_filter(
$this->extends,
function ($cn) { return strpos($cn, 'Predis\\') === 0; }
);
}
public function getDependencies($all = false)
{
return array_merge(
$this->getImplementedInterfaces($all),
$this->getExtendedClasses($all)
);
}
public function getNamespace()
{
return $this->namespace;
}
public function getFile()
{
return $this->file;
}
public function getName()
{
return $this->name;
}
public function getFQN()
{
return (string)$this->getNamespace() . '\\' . $this->name;
}
public function getPhpCode()
{
return $this->body;
}
public function __toString()
{
return "class " . $this->getName() . '{ ... }';
}
}
/* -------------------------------------------------------------------------- */
$options = CommandLine::getOptions();
$predisFile = PredisFile::from($options['source'], $options['exclude']);
$predisFile->saveTo($options['output']);
+7 -15
View File
@@ -1,7 +1,7 @@
{
"name": "predis/predis",
"type": "library",
"description": "Flexible and feature-complete Redis client for PHP and HHVM",
"description": "A flexible and feature-complete Redis client for PHP.",
"keywords": ["nosql", "redis", "predis"],
"homepage": "http://github.com/predis/predis",
"license": "MIT",
@@ -28,11 +28,10 @@
}
],
"require": {
"php": ">=5.3.9"
"php": "^7.2 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "~4.8",
"cweagans/composer-patches": "^1.6"
"phpunit/phpunit": "^8.0 || ~9.4.4"
},
"suggest": {
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
@@ -44,15 +43,8 @@
}
},
"extra": {
"composer-exit-on-patch-failure": true,
"patches": {
"phpunit/phpunit-mock-objects": {
"Fix PHP 7 and 8 compatibility": "./tests/phpunit_mock_objects.patch"
},
"phpunit/phpunit": {
"Fix PHP 7 compatibility": "./tests/phpunit_php7.patch",
"Fix PHP 8 compatibility": "./tests/phpunit_php8.patch"
}
}
}
"branch-alias": {
"dev-main": "2.0-dev"
}
}
}
+3 -3
View File
@@ -18,7 +18,7 @@ require __DIR__.'/shared.php';
use Predis\Cluster\Distributor\DistributorInterface;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\Cluster\PredisStrategy;
use Predis\Connection\Aggregate\PredisCluster;
use Predis\Connection\Cluster\PredisCluster;
class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
{
@@ -104,8 +104,8 @@ for ($i = 0; $i < 100; ++$i) {
$client->get("key:$i");
}
$server1 = $client->getClientFor('first')->info();
$server2 = $client->getClientFor('second')->info();
$server1 = $client->getClientBy('alias', 'first')->info();
$server2 = $client->getClientBy('alias', 'second')->info();
if (isset($server1['Keyspace'], $server2['Keyspace'])) {
$server1 = $server1['Keyspace'];
+2 -2
View File
@@ -51,7 +51,7 @@ class EventsListener implements Countable
return $this->events;
}
public function __invoke($payload)
public function __invoke($payload, $dispatcher)
{
$this->events[] = $payload;
}
@@ -61,7 +61,7 @@ class EventsListener implements Countable
$dispatcher->attachCallback('events', ($events = new EventsListener()));
// Attach a function to control the dispatcher loop termination with a message.
$dispatcher->attachCallback('control', function ($payload) use ($dispatcher) {
$dispatcher->attachCallback('control', function ($payload, $dispatcher) {
if ($payload === 'terminate_dispatcher') {
$dispatcher->stop();
}
+7 -11
View File
@@ -13,11 +13,10 @@ require __DIR__.'/shared.php';
// This example will not work with versions of Redis < 2.6.
//
// Additionally to the EVAL command defined in the current development profile,
// the Predis\Command\ScriptCommand class can be used to build an higher level
// abstraction for "scriptable" commands so that they will appear just like any
// other command on the client-side. This is a quick example used to implement
// INCREX.
// Additionally to the EVAL command, the Predis\Command\ScriptCommand class can
// be used to leverage an higher level abstraction for Lua scripting that makes
// scripts appear just like any other command on the client-side. This is basic
// example on how a script-based INCREX command can be defined:
use Predis\Command\ScriptCommand;
@@ -50,12 +49,9 @@ LUA;
}
$client = new Predis\Client($single_server, array(
'profile' => function ($options) {
$profile = $options->getDefault('profile');
$profile->defineCommand('increxby', 'IncrementExistingKeysBy');
return $profile;
},
'commands' => array(
'increxby' => 'IncrementExistingKeysBy',
),
));
$client->mset('foo', 10, 'foobar', 100);
+2 -3
View File
@@ -13,7 +13,7 @@ require __DIR__.'/shared.php';
use Predis\Collection\Iterator;
// Starting from Redis 2.8, clients can iterate incrementally over collections
// Starting with Redis 2.8, clients can iterate incrementally over collections
// without blocking the server like it happens when a command such as KEYS is
// executed on a Redis instance storing millions of keys. These commands are:
//
@@ -29,8 +29,7 @@ use Predis\Collection\Iterator;
// See http://redis.io/commands/scan for more details.
//
// Create a client using `2.8` as a server profile (needs Redis 2.8!)
$client = new Predis\Client($single_server, array('profile' => '2.8'));
$client = new Predis\Client($single_server);
// Prepare some keys for our example
$client->del('predis:set', 'predis:zset', 'predis:hash');
+10 -13
View File
@@ -14,12 +14,12 @@ require __DIR__.'/shared.php';
// Predis allows to set Lua scripts as read-only operations for replication.
// This works for both EVAL and EVALSHA and also for the client-side abstraction
// built upon them (Predis\Command\ScriptCommand). This example shows a slightly
// more complex configuration that injects a new script command in the server
// profile used by the new client instance and marks it marks it as a read-only
// operation for replication so that it will be executed on slaves.
// more complex configuration that injects a new script command in the command
// factory used by the client and marks it as a read-only operation so that it
// will be executed on slaves.
use Predis\Command\ScriptCommand;
use Predis\Connection\Aggregate\MasterSlaveReplication;
use Predis\Connection\Replication\MasterSlaveReplication;
use Predis\Replication\ReplicationStrategy;
// ------------------------------------------------------------------------- //
@@ -47,17 +47,14 @@ LUA;
// ------------------------------------------------------------------------- //
$parameters = array(
'tcp://127.0.0.1:6379/?alias=master',
'tcp://127.0.0.1:6380/?alias=slave',
'tcp://127.0.0.1:6381?role=master&database=15',
'tcp://127.0.0.1:6382?role=slave&alias=slave-01&database=15',
);
$options = array(
'profile' => function ($options, $option) {
$profile = $options->getDefault($option);
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
return $profile;
},
'commands' => array(
'hmgetall' => 'HashMultipleGetAll',
),
'replication' => function () {
$strategy = new ReplicationStrategy();
$strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
@@ -79,7 +76,7 @@ $client = new Predis\Client($parameters, $options);
$hashes = $client->hmgetall('metavars', 'servers');
$replication = $client->getConnection();
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionById('slave');
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionByAlias('slave-01');
echo 'Is still on slave? ', $stillOnSlave ? 'YES!' : 'NO!', PHP_EOL;
var_export($hashes);
+3 -3
View File
@@ -39,17 +39,17 @@ $client = new Predis\Client($sentinels, array(
// Read operation.
$exists = $client->exists('foo') ? 'yes' : 'no';
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL;
echo "Does 'foo' exist on {$current->role}? $exists.", PHP_EOL;
// Write operation.
$client->set('foo', 'bar');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL;
echo "Now 'foo' has been set to 'bar' on {$current->role}!", PHP_EOL;
// Read operation.
$bar = $client->get('foo');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL;
echo "We fetched 'foo' from {$current->role} and its value is '$bar'.", PHP_EOL;
/* OUTPUT:
Does 'foo' exist on slave-127.0.0.1:6381? yes.
+6 -6
View File
@@ -22,28 +22,28 @@ require __DIR__.'/shared.php';
//
$parameters = array(
'tcp://127.0.0.1:6379?database=15&alias=master',
'tcp://127.0.0.1:6380?database=15&alias=slave',
'tcp://127.0.0.1:6381?role=master&database=15',
'tcp://127.0.0.1:6382?role=slave&database=15',
);
$options = array('replication' => true);
$options = array('replication' => 'predis');
$client = new Predis\Client($parameters, $options);
// Read operation.
$exists = $client->exists('foo') ? 'yes' : 'no';
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL;
echo "Does 'foo' exist on {$current->role}? $exists.", PHP_EOL;
// Write operation.
$client->set('foo', 'bar');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL;
echo "Now 'foo' has been set to 'bar' on {$current->role}!", PHP_EOL;
// Read operation.
$bar = $client->get('foo');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL;
echo "We fetched 'foo' from {$current->role} and its value is '$bar'.", PHP_EOL;
/* OUTPUT:
Does 'foo' exist on slave? yes.
-36
View File
@@ -1,36 +0,0 @@
; This file is meant to be used with Onion http://c9s.github.com/Onion/
; For instructions on how to build a PEAR package of Predis please follow
; the instructions at this URL:
;
; https://github.com/c9s/Onion#a-quick-tutorial-for-building-pear-package
;
[package]
name = "Predis"
desc = "Flexible and feature-complete Redis client for PHP and HHVM"
homepage = "http://github.com/nrk/predis"
license = "MIT"
version = "1.1.4"
stability = "stable"
channel = "pear.nrk.io"
author = "Daniele Alessandri \"nrk\" <suppakilla@gmail.com>"
[require]
php = ">= 5.3.9"
pearinstaller = "1.4.1"
[roles]
*.xml.dist = test
*.md = doc
LICENSE = doc
[optional phpiredis]
hint = "Add support for faster protocol handling with phpiredis"
extensions[] = socket
extensions[] = phpiredis
[optional webdis]
hint = "Add support for Webdis"
extensions[] = curl
extensions[] = phpiredis
-3
View File
@@ -8,8 +8,6 @@
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
syntaxCheck="true"
beStrictAboutTestSize="true"
beStrictAboutTestsThatDoNotTestAnything="true">
<testsuites>
@@ -38,7 +36,6 @@
<php>
<!-- Redis -->
<const name="REDIS_SERVER_VERSION" value="3.2" />
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
<const name="REDIS_SERVER_PORT" value="6379" />
<const name="REDIS_SERVER_DBNUM" value="15" />
-54
View File
@@ -1,54 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
syntaxCheck="true"
beStrictAboutTestSize="true"
beStrictAboutTestsThatDoNotTestAnything="true">
<testsuites>
<testsuite name="Predis Test Suite">
<directory>tests/Predis/</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>ext-phpiredis</group>
<group>ext-curl</group>
<group>realm-webdis</group>
<!-- <group>connected</group> -->
<!-- <group>disconnected</group> -->
<!-- <group>commands</group> -->
<!-- <group>slow</group> -->
</exclude>
</groups>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-text" target="php://stdout" />
</logging>
<php>
<!-- Redis -->
<const name="REDIS_SERVER_VERSION" value="3.2" />
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
<const name="REDIS_SERVER_PORT" value="6379" />
<const name="REDIS_SERVER_DBNUM" value="15" />
<!-- Webdis -->
<const name="WEBDIS_SERVER_HOST" value="127.0.0.1" />
<const name="WEBDIS_SERVER_PORT" value="7379" />
</php>
</phpunit>
+1
View File
@@ -16,6 +16,7 @@ namespace Predis;
*
* @author Eric Naeseth <eric@thumbtack.com>
* @author Daniele Alessandri <suppakilla@gmail.com>
* @codeCoverageIgnore
*/
class Autoloader
{
+102 -117
View File
@@ -16,8 +16,8 @@ use Predis\Command\RawCommand;
use Predis\Command\ScriptCommand;
use Predis\Configuration\Options;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\AggregateConnectionInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\Parameters;
use Predis\Connection\ParametersInterface;
use Predis\Monitor\Consumer as MonitorConsumer;
use Predis\Pipeline\Pipeline;
@@ -40,11 +40,16 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
*/
class Client implements ClientInterface, \IteratorAggregate
{
const VERSION = '1.1.4';
const VERSION = '2.0.3';
protected $connection;
protected $options;
private $profile;
/** @var OptionsInterface */
private $options;
/** @var ConnectionInterface */
private $connection;
/** @var Command\FactoryInterface */
private $commands;
/**
* @param mixed $parameters Connection parameters for one or more servers.
@@ -52,131 +57,101 @@ class Client implements ClientInterface, \IteratorAggregate
*/
public function __construct($parameters = null, $options = null)
{
$this->options = $this->createOptions($options ?: array());
$this->connection = $this->createConnection($parameters ?: array());
$this->profile = $this->options->profile;
$this->options = static::createOptions($options ?? new Options);
$this->connection = static::createConnection($this->options, $parameters ?? new Parameters);
$this->commands = $this->options->commands;
}
/**
* Creates a new instance of Predis\Configuration\Options from different
* types of arguments or simply returns the passed argument if it is an
* instance of Predis\Configuration\OptionsInterface.
* Creates a new set of client options for the client.
*
* @param mixed $options Client options.
* @param array|OptionsInterface $options Set of client options
*
* @throws \InvalidArgumentException
*
* @return OptionsInterface
*/
protected function createOptions($options)
protected static function createOptions($options)
{
if (is_array($options)) {
return new Options($options);
}
if ($options instanceof OptionsInterface) {
} elseif ($options instanceof OptionsInterface) {
return $options;
} else {
throw new \InvalidArgumentException('Invalid type for client options');
}
throw new \InvalidArgumentException('Invalid type for client options.');
}
/**
* Creates single or aggregate connections from different types of arguments
* (string, array) or returns the passed argument if it is an instance of a
* class implementing Predis\Connection\ConnectionInterface.
* Creates single or aggregate connections from supplied arguments.
*
* Accepted types for connection parameters are:
* This method accepts the following types to create a connection instance:
*
* - Instance of Predis\Connection\ConnectionInterface.
* - Instance of Predis\Connection\ParametersInterface.
* - Array
* - String
* - Callable
* - Array (dictionary: single connection, indexed: aggregate connections)
* - String (URI for a single connection)
* - Callable (connection initializer callback)
* - Instance of Predis\Connection\ParametersInterface (used as-is)
* - Instance of Predis\Connection\ConnectionInterface (returned as-is)
*
* @param mixed $parameters Connection parameters or connection instance.
* When a callable is passed, it receives the original set of client options
* and must return an instance of Predis\Connection\ConnectionInterface.
*
* Connections are created using the connection factory (in case of single
* connections) or a specialized aggregate connection initializer (in case
* of cluster and replication) retrieved from the supplied client options.
*
* @param OptionsInterface $options Client options container
* @param mixed $parameters Connection parameters
*
* @throws \InvalidArgumentException
*
* @return ConnectionInterface
*/
protected function createConnection($parameters)
protected static function createConnection(OptionsInterface $options, $parameters)
{
if ($parameters instanceof ConnectionInterface) {
return $parameters;
}
if ($parameters instanceof ParametersInterface || is_string($parameters)) {
return $this->options->connections->create($parameters);
return $options->connections->create($parameters);
}
if (is_array($parameters)) {
if (!isset($parameters[0])) {
return $this->options->connections->create($parameters);
}
$options = $this->options;
if ($options->defined('aggregate')) {
$initializer = $this->getConnectionInitializerWrapper($options->aggregate);
$connection = $initializer($parameters, $options);
} elseif ($options->defined('replication')) {
$replication = $options->replication;
if ($replication instanceof AggregateConnectionInterface) {
$connection = $replication;
$options->connections->aggregate($connection, $parameters);
} else {
$initializer = $this->getConnectionInitializerWrapper($replication);
$connection = $initializer($parameters, $options);
}
return $options->connections->create($parameters);
} elseif ($options->defined('cluster') && $initializer = $options->cluster) {
return $initializer($parameters, true);
} elseif ($options->defined('replication') && $initializer = $options->replication) {
return $initializer($parameters, true);
} elseif ($options->defined('aggregate') && $initializer = $options->aggregate) {
return $initializer($parameters, false);
} else {
$connection = $options->cluster;
$options->connections->aggregate($connection, $parameters);
throw new \InvalidArgumentException(
'Array of connection parameters requires `cluster`, `replication` or `aggregate` client option'
);
}
return $connection;
}
if (is_callable($parameters)) {
$initializer = $this->getConnectionInitializerWrapper($parameters);
$connection = $initializer($this->options);
$connection = call_user_func($parameters, $options);
if (!$connection instanceof ConnectionInterface) {
throw new \InvalidArgumentException('Callable parameters must return a valid connection');
}
return $connection;
}
throw new \InvalidArgumentException('Invalid type for connection parameters.');
}
/**
* Wraps a callable to make sure that its returned value represents a valid
* connection type.
*
* @param mixed $callable
*
* @return \Closure
*/
protected function getConnectionInitializerWrapper($callable)
{
return function () use ($callable) {
$connection = call_user_func_array($callable, func_get_args());
if (!$connection instanceof ConnectionInterface) {
throw new \UnexpectedValueException(
'The callable connection initializer returned an invalid type.'
);
}
return $connection;
};
throw new \InvalidArgumentException('Invalid type for connection parameters');
}
/**
* {@inheritdoc}
*/
public function getProfile()
public function getCommandFactory()
{
return $this->profile;
return $this->commands;
}
/**
@@ -188,23 +163,53 @@ class Client implements ClientInterface, \IteratorAggregate
}
/**
* Creates a new client instance for the specified connection ID or alias,
* only when working with an aggregate connection (cluster, replication).
* The new client instances uses the same options of the original one.
* Creates a new client using a specific underlying connection.
*
* @param string $connectionID Identifier of a connection.
* This method allows to create a new client instance by picking a specific
* connection out of an aggregate one, with the same options of the original
* client instance.
*
* @throws \InvalidArgumentException
* The specified selector defines which logic to use to look for a suitable
* connection by the specified value. Supported selectors are:
*
* @return Client
* - `id`
* - `key`
* - `slot`
* - `command`
* - `alias`
* - `role`
*
* Internally the client relies on duck-typing and follows this convention:
*
* $selector string => getConnectionBy$selector($value) method
*
* This means that support for specific selectors may vary depending on the
* actual logic implemented by connection classes and there is no interface
* binding a connection class to implement any of these.
*
* @param string $selector Type of selector.
* @param mixed $value Value to be used by the selector.
*
* @return ClientInterface
*/
public function getClientFor($connectionID)
public function getClientBy($selector, $value)
{
if (!$connection = $this->getConnectionById($connectionID)) {
throw new \InvalidArgumentException("Invalid connection ID: $connectionID.");
$selector = strtolower($selector);
if (!in_array($selector, array('id', 'key', 'slot', 'role', 'alias', 'command'))) {
throw new \InvalidArgumentException("Invalid selector type: `$selector`");
}
return new static($connection, $this->options);
if (!method_exists($this->connection, $method = "getConnectionBy$selector")) {
$class = get_class($this->connection);
throw new \InvalidArgumentException("Selecting connection by $selector is not supported by $class");
}
if (!$connection = $this->connection->$method($value)) {
throw new \InvalidArgumentException("Cannot find a connection by $selector matching `$value`");
}
return new static($connection, $this->getOptions());
}
/**
@@ -252,27 +257,6 @@ class Client implements ClientInterface, \IteratorAggregate
return $this->connection;
}
/**
* Retrieves the specified connection from the aggregate connection when the
* client is in cluster or replication mode.
*
* @param string $connectionID Index or alias of the single connection.
*
* @throws NotSupportedException
*
* @return Connection\NodeConnectionInterface
*/
public function getConnectionById($connectionID)
{
if (!$this->connection instanceof AggregateConnectionInterface) {
throw new NotSupportedException(
'Retrieving connections by ID is supported only by aggregate connections.'
);
}
return $this->connection->getConnectionById($connectionID);
}
/**
* Executes a command without filtering its arguments, parsing the response,
* applying any prefix to keys or throwing exceptions on Redis errors even
@@ -289,9 +273,10 @@ class Client implements ClientInterface, \IteratorAggregate
public function executeRaw(array $arguments, &$error = null)
{
$error = false;
$commandID = array_shift($arguments);
$response = $this->connection->executeCommand(
new RawCommand($arguments)
new RawCommand($commandID, $arguments)
);
if ($response instanceof ResponseInterface) {
@@ -320,7 +305,7 @@ class Client implements ClientInterface, \IteratorAggregate
*/
public function createCommand($commandID, $arguments = array())
{
return $this->profile->createCommand($commandID, $arguments);
return $this->commands->create($commandID, $arguments);
}
/**
@@ -354,10 +339,7 @@ class Client implements ClientInterface, \IteratorAggregate
protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
{
if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
$eval = $this->createCommand('EVAL');
$eval->setRawArguments($command->getEvalArguments());
$response = $this->executeCommand($eval);
$response = $this->executeCommand($command->getEvalCommand());
if (!$response instanceof ResponseInterface) {
$response = $command->parseResponse($response);
@@ -400,9 +382,11 @@ class Client implements ClientInterface, \IteratorAggregate
return $this->$initializer($arg0, $arg1);
// @codeCoverageIgnoreStart
default:
return $this->$initializer($this, $argv);
}
// @codeCoverageIgnoreEnd
}
/**
@@ -529,6 +513,7 @@ class Client implements ClientInterface, \IteratorAggregate
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
$clients = array();
+1 -1
View File
@@ -118,7 +118,7 @@ use Predis\Command\CommandInterface;
* @method $this zremrangebyrank($key, $start, $stop)
* @method $this zremrangebyscore($key, $min, $max)
* @method $this zrevrange($key, $start, $stop, array $options = null)
* @method $this zrevrangebyscore($key, $min, $max, array $options = null)
* @method $this zrevrangebyscore($key, $max, $min, array $options = null)
* @method $this zrevrank($key, $member)
* @method $this zunionstore($destination, array|string $keys, array $options = null)
* @method $this zscore($key, $member)
+162 -152
View File
@@ -12,9 +12,10 @@
namespace Predis;
use Predis\Command\CommandInterface;
use Predis\Command\FactoryInterface;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Profile\ProfileInterface;
use Predis\Response\Status;
/**
* Interface defining a client able to execute commands against Redis.
@@ -24,165 +25,174 @@ use Predis\Profile\ProfileInterface;
* and more friendly interface to ease programming which is described in the
* following list of methods:
*
* @method int del(array|string $keys)
* @method string|null dump($key)
* @method int exists($key)
* @method int expire($key, $seconds)
* @method int expireat($key, $timestamp)
* @method array keys($pattern)
* @method int move($key, $db)
* @method mixed object($subcommand, $key)
* @method int persist($key)
* @method int pexpire($key, $milliseconds)
* @method int pexpireat($key, $timestamp)
* @method int pttl($key)
* @method string|null randomkey()
* @method mixed rename($key, $target)
* @method int renamenx($key, $target)
* @method array scan($cursor, array $options = null)
* @method array sort($key, array $options = null)
* @method int ttl($key)
* @method mixed type($key)
* @method int append($key, $value)
* @method int bitcount($key, $start = null, $end = null)
* @method int bitop($operation, $destkey, $key)
* @method array|null bitfield($key, $subcommand, ...$subcommandArg)
* @method int bitpos($key, $bit, $start = null, $end = null)
* @method int decr($key)
* @method int decrby($key, $decrement)
* @method string|null get($key)
* @method int getbit($key, $offset)
* @method string getrange($key, $start, $end)
* @method string|null getset($key, $value)
* @method int incr($key)
* @method int incrby($key, $increment)
* @method string incrbyfloat($key, $increment)
* @method array mget(array $keys)
* @method mixed mset(array $dictionary)
* @method int msetnx(array $dictionary)
* @method mixed psetex($key, $milliseconds, $value)
* @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method int setbit($key, $offset, $value)
* @method int setex($key, $seconds, $value)
* @method int setnx($key, $value)
* @method int setrange($key, $offset, $value)
* @method int strlen($key)
* @method int hdel($key, array $fields)
* @method int hexists($key, $field)
* @method string|null hget($key, $field)
* @method array hgetall($key)
* @method int hincrby($key, $field, $increment)
* @method string hincrbyfloat($key, $field, $increment)
* @method array hkeys($key)
* @method int hlen($key)
* @method array hmget($key, array $fields)
* @method mixed hmset($key, array $dictionary)
* @method array hscan($key, $cursor, array $options = null)
* @method int hset($key, $field, $value)
* @method int hsetnx($key, $field, $value)
* @method array hvals($key)
* @method int hstrlen($key, $field)
* @method array|null blpop(array|string $keys, $timeout)
* @method array|null brpop(array|string $keys, $timeout)
* @method string|null brpoplpush($source, $destination, $timeout)
* @method string|null lindex($key, $index)
* @method int linsert($key, $whence, $pivot, $value)
* @method int llen($key)
* @method string|null lpop($key)
* @method int lpush($key, array $values)
* @method int lpushx($key, array $values)
* @method array lrange($key, $start, $stop)
* @method int lrem($key, $count, $value)
* @method mixed lset($key, $index, $value)
* @method mixed ltrim($key, $start, $stop)
* @method string|null rpop($key)
* @method string|null rpoplpush($source, $destination)
* @method int rpush($key, array $values)
* @method int rpushx($key, array $values)
* @method int sadd($key, array $members)
* @method int scard($key)
* @method array sdiff(array|string $keys)
* @method int sdiffstore($destination, array|string $keys)
* @method array sinter(array|string $keys)
* @method int sinterstore($destination, array|string $keys)
* @method int sismember($key, $member)
* @method array smembers($key)
* @method int smove($source, $destination, $member)
* @method string|null spop($key, $count = null)
* @method string|null srandmember($key, $count = null)
* @method int srem($key, $member)
* @method array sscan($key, $cursor, array $options = null)
* @method array sunion(array|string $keys)
* @method int sunionstore($destination, array|string $keys)
* @method int zadd($key, array $membersAndScoresDictionary)
* @method int zcard($key)
* @method string zcount($key, $min, $max)
* @method string zincrby($key, $increment, $member)
* @method int zinterstore($destination, array|string $keys, array $options = null)
* @method array zrange($key, $start, $stop, array $options = null)
* @method array zrangebyscore($key, $min, $max, array $options = null)
* @method int|null zrank($key, $member)
* @method int zrem($key, $member)
* @method int zremrangebyrank($key, $start, $stop)
* @method int zremrangebyscore($key, $min, $max)
* @method array zrevrange($key, $start, $stop, array $options = null)
* @method array zrevrangebyscore($key, $max, $min, array $options = null)
* @method int|null zrevrank($key, $member)
* @method int zunionstore($destination, array|string $keys, array $options = null)
* @method string|null zscore($key, $member)
* @method array zscan($key, $cursor, array $options = null)
* @method array zrangebylex($key, $start, $stop, array $options = null)
* @method array zrevrangebylex($key, $start, $stop, array $options = null)
* @method int zremrangebylex($key, $min, $max)
* @method int zlexcount($key, $min, $max)
* @method int pfadd($key, array $elements)
* @method mixed pfmerge($destinationKey, array|string $sourceKeys)
* @method int pfcount(array|string $keys)
* @method mixed pubsub($subcommand, $argument)
* @method int publish($channel, $message)
* @method mixed discard()
* @method array|null exec()
* @method mixed multi()
* @method mixed unwatch()
* @method mixed watch($key)
* @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method mixed script($subcommand, $argument = null)
* @method mixed auth($password)
* @method string echo($message)
* @method mixed ping($message = null)
* @method mixed select($database)
* @method mixed bgrewriteaof()
* @method mixed bgsave()
* @method mixed client($subcommand, $argument = null)
* @method mixed config($subcommand, $argument = null)
* @method int dbsize()
* @method mixed flushall()
* @method mixed flushdb()
* @method array info($section = null)
* @method int lastsave()
* @method mixed save()
* @method mixed slaveof($host, $port)
* @method mixed slowlog($subcommand, $argument = null)
* @method array time()
* @method array command()
* @method int geoadd($key, $longitude, $latitude, $member)
* @method array geohash($key, array $members)
* @method array geopos($key, array $members)
* @method string|null geodist($key, $member1, $member2, $unit = null)
* @method array georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method array georadiusbymember($key, $member, $radius, $unit, array $options = null)
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
* @method string|null dump(string $key)
* @method int exists(string $key)
* @method int expire(string $key, int $seconds)
* @method int expireat(string $key, int $timestamp)
* @method array keys(string $pattern)
* @method int move(string $key, int $db)
* @method mixed object($subcommand, string $key)
* @method int persist(string $key)
* @method int pexpire(string $key, int $milliseconds)
* @method int pexpireat(string $key, int $timestamp)
* @method int pttl(string $key)
* @method string|null randomkey()
* @method mixed rename(string $key, string $target)
* @method int renamenx(string $key, string $target)
* @method array scan($cursor, array $options = null)
* @method array sort(string $key, array $options = null)
* @method int ttl(string $key)
* @method mixed type(string $key)
* @method int append(string $key, $value)
* @method int bitcount(string $key, $start = null, $end = null)
* @method int bitop($operation, $destkey, $key)
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
* @method int bitpos(string $key, $bit, $start = null, $end = null)
* @method int decr(string $key)
* @method int decrby(string $key, int $decrement)
* @method string|null get(string $key)
* @method int getbit(string $key, $offset)
* @method string getrange(string $key, $start, $end)
* @method string|null getset(string $key, $value)
* @method int incr(string $key)
* @method int incrby(string $key, int $increment)
* @method string incrbyfloat(string $key, int|float $increment)
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed mset(array $dictionary)
* @method int msetnx(array $dictionary)
* @method Status psetex(string $key, $milliseconds, $value)
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method int setbit(string $key, $offset, $value)
* @method Status setex(string $key, $seconds, $value)
* @method int setnx(string $key, $value)
* @method int setrange(string $key, $offset, $value)
* @method int strlen(string $key)
* @method int hdel(string $key, array $fields)
* @method int hexists(string $key, string $field)
* @method string|null hget(string $key, string $field)
* @method array hgetall(string $key)
* @method int hincrby(string $key, string $field, int $increment)
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
* @method array hkeys(string $key)
* @method int hlen(string $key)
* @method array hmget(string $key, array $fields)
* @method mixed hmset(string $key, array $dictionary)
* @method array hscan(string $key, $cursor, array $options = null)
* @method int hset(string $key, string $field, string $value)
* @method int hsetnx(string $key, string $field, string $value)
* @method array hvals(string $key)
* @method int hstrlen(string $key, string $field)
* @method array|null blpop(array|string $keys, int|float $timeout)
* @method array|null brpop(array|string $keys, int|float $timeout)
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
* @method string|null lindex(string $key, int $index)
* @method int linsert(string $key, $whence, $pivot, $value)
* @method int llen(string $key)
* @method string|null lpop(string $key)
* @method int lpush(string $key, array $values)
* @method int lpushx(string $key, array $values)
* @method string[] lrange(string $key, int $start, int $stop)
* @method int lrem(string $key, int $count, string $value)
* @method mixed lset(string $key, int $index, string $value)
* @method mixed ltrim(string $key, int $start, int $stop)
* @method string|null rpop(string $key)
* @method string|null rpoplpush(string $source, string $destination)
* @method int rpush(string $key, array $values)
* @method int rpushx(string $key, array $values)
* @method int sadd(string $key, array $members)
* @method int scard(string $key)
* @method string[] sdiff(array|string $keys)
* @method int sdiffstore(string $destination, array|string $keys)
* @method string[] sinter(array|string $keys)
* @method int sinterstore(string $destination, array|string $keys)
* @method int sismember(string $key, string $member)
* @method string[] smembers(string $key)
* @method int smove(string $source, string $destination, string $member)
* @method string|array|null spop(string $key, int $count = null)
* @method string|null srandmember(string $key, int $count = null)
* @method int srem(string $key, array|string $member)
* @method array sscan(string $key, int $cursor, array $options = null)
* @method string[] sunion(array|string $keys)
* @method int sunionstore(string $destination, array|string $keys)
* @method int touch(string[]|string $keyOrKeys, string ...$keys = null)
* @method string xadd(string $key, array $dictionary, string $id = '*', array $options = null)
* @method int xdel(string $key, string ...$id)
* @method int xlen(string $key)
* @method array xrevrange(string $key, string $end, string $start, ?int $count = null)
* @method array xrange(string $key, string $start, string $end, ?int $count = null)
* @method string xtrim(string $key, array|string $strategy, string $threshold, array $options = null)
* @method int zadd(string $key, array $membersAndScoresDictionary)
* @method int zcard(string $key)
* @method string zcount(string $key, int|string $min, int|string $max)
* @method string zincrby(string $key, int $increment, string $member)
* @method int zinterstore(string $destination, array|string $keys, array $options = null)
* @method array zpopmin(string $key, int $count = 1)
* @method array zpopmax(string $key, int $count = 1)
* @method array zrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null)
* @method int|null zrank(string $key, string $member)
* @method int zrem(string $key, string ...$member)
* @method int zremrangebyrank(string $key, int|string $start, int|string $stop)
* @method int zremrangebyscore(string $key, int|string $min, int|string $max)
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
* @method int|null zrevrank(string $key, string $member)
* @method int zunionstore(string $destination, array|string $keys, array $options = null)
* @method string|null zscore(string $key, string $member)
* @method array zscan(string $key, int $cursor, array $options = null)
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
* @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null)
* @method int zremrangebylex(string $key, string $min, string $max)
* @method int zlexcount(string $key, string $min, string $max)
* @method int pfadd(string $key, array $elements)
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed pubsub($subcommand, $argument)
* @method int publish($channel, $message)
* @method mixed discard()
* @method array|null exec()
* @method mixed multi()
* @method mixed unwatch()
* @method mixed watch(string $key)
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed script($subcommand, $argument = null)
* @method mixed auth(string $password)
* @method string echo(string $message)
* @method mixed ping(string $message = null)
* @method mixed select(int $database)
* @method mixed bgrewriteaof()
* @method mixed bgsave()
* @method mixed client($subcommand, $argument = null)
* @method mixed config($subcommand, $argument = null)
* @method int dbsize()
* @method mixed flushall()
* @method mixed flushdb()
* @method array info($section = null)
* @method int lastsave()
* @method mixed save()
* @method mixed slaveof(string $host, int $port)
* @method mixed slowlog($subcommand, $argument = null)
* @method array time()
* @method array command()
* @method int geoadd(string $key, $longitude, $latitude, $member)
* @method array geohash(string $key, array $members)
* @method array geopos(string $key, array $members)
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface ClientInterface
{
/**
* Returns the server profile used by the client.
* Returns the command factory used by the client.
*
* @return ProfileInterface
* @return FactoryInterface
*/
public function getProfile();
public function getCommandFactory();
/**
* Returns the client options specified upon initialization.
+1 -2
View File
@@ -239,9 +239,8 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
public function get($value)
{
$hash = $this->hash($value);
$node = $this->getByHash($hash);
return $node;
return $this->getByHash($hash);
}
/**
+44
View File
@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Hash;
use Predis\NotSupportedException;
/**
* Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PhpiredisCRC16 implements HashGeneratorInterface
{
/**
*
*/
public function __construct()
{
if (!function_exists('phpiredis_utils_crc16')) {
// @codeCoverageIgnoreStart
throw new NotSupportedException(
'This hash generator requires a compatible version of ext-phpiredis'
);
// @codeCoverageIgnoreEnd
}
}
/**
* {@inheritdoc}
*/
public function hash($value)
{
return phpiredis_utils_crc16($value);
}
}
+1 -2
View File
@@ -40,9 +40,8 @@ class PredisStrategy extends ClusterStrategy
{
$key = $this->extractKeyTag($key);
$hash = $this->distributor->hash($key);
$slot = $this->distributor->getSlot($hash);
return $slot;
return $this->distributor->getSlot($hash);
}
/**
+3 -5
View File
@@ -41,9 +41,8 @@ class RedisStrategy extends ClusterStrategy
public function getSlotByKey($key)
{
$key = $this->extractKeyTag($key);
$slot = $this->hashGenerator->hash($key) & 0x3FFF;
return $slot;
return $this->hashGenerator->hash($key) & 0x3FFF;
}
/**
@@ -51,8 +50,7 @@ class RedisStrategy extends ClusterStrategy
*/
public function getDistributor()
{
throw new NotSupportedException(
'This cluster strategy does not provide an external distributor'
);
$class = get_class($this);
throw new NotSupportedException("$class does not provide an external distributor");
}
}
+201
View File
@@ -0,0 +1,201 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster;
/**
* Slot map for redis-cluster.
*/
class SlotMap implements \ArrayAccess, \IteratorAggregate, \Countable
{
private $slots = array();
/**
* Checks if the given slot is valid.
*
* @param int $first Slot index.
*
* @return bool
*/
public static function isValid($slot)
{
return $slot >= 0x0000 && $slot <= 0x3FFF;
}
/**
* Checks if the given slot range is valid.
*
* @param int $first Initial slot of the range.
* @param int $last Last slot of the range.
*
* @return bool
*/
public static function isValidRange($first, $last)
{
return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last;
}
/**
* Resets the slot map.
*/
public function reset()
{
$this->slots = array();
}
/**
* Checks if the slot map is empty.
*
* @return bool
*/
public function isEmpty()
{
return empty($this->slots);
}
/**
* Returns the current slot map as a dictionary of $slot => $node.
*
* The order of the slots in the dictionary is not guaranteed.
*
* @return array
*/
public function toArray()
{
return $this->slots;
}
/**
* Returns the list of unique nodes in the slot map.
*
* @return array
*/
public function getNodes()
{
return array_keys(array_flip($this->slots));
}
/**
* Assigns the specified slot range to a node.
*
* @param int $first Initial slot of the range.
* @param int $last Last slot of the range.
* @param NodeConnectionInterface|string $connection ID or connection instance.
*
* @throws \OutOfBoundsException
*/
public function setSlots($first, $last, $connection)
{
if (!static::isValidRange($first, $last)) {
throw new \OutOfBoundsException("Invalid slot range $first-$last for `$connection`");
}
$this->slots += array_fill($first, $last - $first + 1, (string) $connection);
}
/**
* Returns the specified slot range.
*
* @param int $first Initial slot of the range.
* @param int $last Last slot of the range.
*
* @return array
*/
public function getSlots($first, $last)
{
if (!static::isValidRange($first, $last)) {
throw new \OutOfBoundsException("Invalid slot range $first-$last");
}
return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null));
}
/**
* Checks if the specified slot is assigned.
*
* @param int $slot Slot index.
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($slot)
{
return isset($this->slots[$slot]);
}
/**
* Returns the node assigned to the specified slot.
*
* @param int $slot Slot index.
*
* @return string
*/
#[\ReturnTypeWillChange]
public function offsetGet($slot)
{
if (isset($this->slots[$slot])) {
return $this->slots[$slot];
}
}
/**
* Assigns the specified slot to a node.
*
* @param int $slot Slot index.
* @param NodeConnectionInterface|string $connection ID or connection instance.
*
* @return string
*/
#[\ReturnTypeWillChange]
public function offsetSet($slot, $connection)
{
if (!static::isValid($slot)) {
throw new \OutOfBoundsException("Invalid slot $slot for `$connection`");
}
$this->slots[(int) $slot] = (string) $connection;
}
/**
* Returns the node assigned to the specified slot.
*
* @param int $slot Slot index.
*
* @return string
*/
#[\ReturnTypeWillChange]
public function offsetUnset($slot)
{
unset($this->slots[$slot]);
}
/**
* Returns the current number of assigned slots.
*
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return count($this->slots);
}
/**
* Returns an iterator over the slot map.
*
* @return \ArrayIterator
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this->slots);
}
}
@@ -65,8 +65,8 @@ abstract class CursorBasedIterator implements \Iterator
*/
protected function requiredCommand(ClientInterface $client, $commandID)
{
if (!$client->getProfile()->supportsCommand($commandID)) {
throw new NotSupportedException("The current profile does not support '$commandID'.");
if (!$client->getCommandFactory()->supports($commandID)) {
throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
}
}
@@ -92,7 +92,7 @@ abstract class CursorBasedIterator implements \Iterator
{
$options = array();
if (strlen($this->match) > 0) {
if (strlen(strval($this->match)) > 0) {
$options['MATCH'] = $this->match;
}
@@ -139,6 +139,7 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->reset();
@@ -148,6 +149,7 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -156,6 +158,7 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -164,6 +167,7 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function next()
{
tryFetch: {
@@ -184,6 +188,7 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+7 -2
View File
@@ -73,8 +73,8 @@ class ListKey implements \Iterator
*/
protected function requiredCommand(ClientInterface $client, $commandID)
{
if (!$client->getProfile()->supportsCommand($commandID)) {
throw new NotSupportedException("The current profile does not support '$commandID'.");
if (!$client->getCommandFactory()->supports($commandID)) {
throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
}
}
@@ -128,6 +128,7 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->reset();
@@ -137,6 +138,7 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -145,6 +147,7 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -153,6 +156,7 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function next()
{
if (!$this->elements && $this->fetchmore) {
@@ -169,6 +173,7 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+2 -14
View File
@@ -21,24 +21,12 @@ abstract class Command implements CommandInterface
private $slot;
private $arguments = array();
/**
* Returns a filtered array of the arguments.
*
* @param array $arguments List of arguments.
*
* @return array
*/
protected function filterArguments(array $arguments)
{
return $arguments;
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$this->arguments = $this->filterArguments($arguments);
$this->arguments = $arguments;
unset($this->slot);
}
@@ -104,7 +92,7 @@ abstract class Command implements CommandInterface
*/
public static function normalizeArguments(array $arguments)
{
if (count($arguments) === 1 && is_array($arguments[0])) {
if (count($arguments) === 1 && isset($arguments[0]) && is_array($arguments[0])) {
return $arguments[0];
}
+1 -1
View File
@@ -73,7 +73,7 @@ interface CommandInterface
/**
* Parses a raw response and returns a PHP object.
*
* @param string $data Binary string containing the whole response.
* @param string|array|null $data Binary string containing the whole response.
*
* @return mixed
*/
+145
View File
@@ -0,0 +1,145 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
use Predis\ClientException;
use Predis\Command\Processor\ProcessorInterface;
/**
* Base command factory class.
*
* This class provides all of the common functionalities required for a command
* factory to create new instances of Redis commands objects. It also allows to
* define or undefine command handler classes for each command ID.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
abstract class Factory implements FactoryInterface
{
protected $commands = [];
protected $processor;
/**
* {@inheritdoc}
*/
public function supports(string ...$commandIDs): bool
{
foreach ($commandIDs as $commandID) {
if ($this->getCommandClass($commandID) === null) {
return false;
}
}
return true;
}
/**
* Returns the FQCN of a class that represents the specified command ID.
*
* @codeCoverageIgnore
*
* @param string $commandID Command ID
*
* @return ?string
*/
public function getCommandClass(string $commandID): ?string
{
if (isset($this->commands[$commandID = strtoupper($commandID)])) {
return $this->commands[$commandID];
}
}
/**
* {@inheritdoc}
*/
public function create(string $commandID, array $arguments = []): CommandInterface
{
if (!$commandClass = $this->getCommandClass($commandID)) {
$commandID = strtoupper($commandID);
throw new ClientException("Command `$commandID` is not a registered Redis command.");
}
$command = new $commandClass();
$command->setArguments($arguments);
if (isset($this->processor)) {
$this->processor->process($command);
}
return $command;
}
/**
* Defines a command in the factory.
*
* Only classes implementing Predis\Command\CommandInterface are allowed to
* handle a command. If the command specified by its ID is already handled
* by the factory, the underlying command class is replaced by the new one.
*
* @param string $commandID Command ID
* @param string $commandClass FQCN of a class implementing Predis\Command\CommandInterface
*
* @throws \InvalidArgumentException
*/
public function define(string $commandID, string $commandClass): void
{
if (!is_a($commandClass, 'Predis\Command\CommandInterface', true)) {
throw new \InvalidArgumentException(
"Class $commandClass must implement Predis\Command\CommandInterface"
);
}
$this->commands[strtoupper($commandID)] = $commandClass;
}
/**
* Undefines a command in the factory.
*
* When the factory already has a class handler associated to the specified
* command ID it is removed from the map of known commands. Nothing happens
* when the command is not handled by the factory.
*
* @param string $commandID Command ID
*/
public function undefine(string $commandID): void
{
unset($this->commands[strtoupper($commandID)]);
}
/**
* Sets a command processor for processing command arguments.
*
* Command processors are used to process and transform arguments of Redis
* commands before their newly created instances are returned to the caller
* of "create()".
*
* A NULL value can be used to effectively unset any processor if previously
* set for the command factory.
*
* @param ProcessorInterface|null $processor Command processor or NULL value.
*/
public function setProcessor(?ProcessorInterface $processor): void
{
$this->processor = $processor;
}
/**
* Returns the current command processor.
*
* @return ?ProcessorInterface
*/
public function getProcessor(): ?ProcessorInterface
{
return $this->processor;
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Command factory interface.
*
* A command factory is used through the library to create instances of commands
* classes implementing Predis\Command\CommandInterface mapped to Redis commands
* by their command ID string (SET, GET, etc...).
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface FactoryInterface
{
/**
* Checks if the command factory supports the specified list of commands.
*
* @param array $commandIDs List of command IDs
*
* @return bool
*/
public function supports(string ...$commandIDs): bool;
/**
* Creates a new command instance.
*
* @param string $commandID Command ID
* @param array $arguments Arguments for the command
*
* @return CommandInterface
*/
public function create(string $commandID, array $arguments = array()): CommandInterface;
}
+143 -131
View File
@@ -31,142 +31,154 @@ class KeyPrefixProcessor implements ProcessorInterface
public function __construct($prefix)
{
$this->prefix = $prefix;
$this->commands = array(
$prefixFirst = static::class . '::first';
$prefixAll = static::class . '::all';
$prefixInterleaved = static::class . '::interleaved';
$prefixSkipFirst = static::class . '::skipFirst';
$prefixSkipLast = static::class . '::skipLast';
$prefixSort = static::class . '::sort';
$prefixEvalKeys = static::class . '::evalKeys';
$prefixZsetStore = static::class . '::zsetStore';
$prefixMigrate = static::class . '::migrate';
$prefixGeoradius = static::class . '::georadius';
$this->commands = [
/* ---------------- Redis 1.2 ---------------- */
'EXISTS' => 'static::all',
'DEL' => 'static::all',
'TYPE' => 'static::first',
'KEYS' => 'static::first',
'RENAME' => 'static::all',
'RENAMENX' => 'static::all',
'EXPIRE' => 'static::first',
'EXPIREAT' => 'static::first',
'TTL' => 'static::first',
'MOVE' => 'static::first',
'SORT' => 'static::sort',
'DUMP' => 'static::first',
'RESTORE' => 'static::first',
'SET' => 'static::first',
'SETNX' => 'static::first',
'MSET' => 'static::interleaved',
'MSETNX' => 'static::interleaved',
'GET' => 'static::first',
'MGET' => 'static::all',
'GETSET' => 'static::first',
'INCR' => 'static::first',
'INCRBY' => 'static::first',
'DECR' => 'static::first',
'DECRBY' => 'static::first',
'RPUSH' => 'static::first',
'LPUSH' => 'static::first',
'LLEN' => 'static::first',
'LRANGE' => 'static::first',
'LTRIM' => 'static::first',
'LINDEX' => 'static::first',
'LSET' => 'static::first',
'LREM' => 'static::first',
'LPOP' => 'static::first',
'RPOP' => 'static::first',
'RPOPLPUSH' => 'static::all',
'SADD' => 'static::first',
'SREM' => 'static::first',
'SPOP' => 'static::first',
'SMOVE' => 'static::skipLast',
'SCARD' => 'static::first',
'SISMEMBER' => 'static::first',
'SINTER' => 'static::all',
'SINTERSTORE' => 'static::all',
'SUNION' => 'static::all',
'SUNIONSTORE' => 'static::all',
'SDIFF' => 'static::all',
'SDIFFSTORE' => 'static::all',
'SMEMBERS' => 'static::first',
'SRANDMEMBER' => 'static::first',
'ZADD' => 'static::first',
'ZINCRBY' => 'static::first',
'ZREM' => 'static::first',
'ZRANGE' => 'static::first',
'ZREVRANGE' => 'static::first',
'ZRANGEBYSCORE' => 'static::first',
'ZCARD' => 'static::first',
'ZSCORE' => 'static::first',
'ZREMRANGEBYSCORE' => 'static::first',
'EXISTS' => $prefixAll,
'DEL' => $prefixAll,
'TYPE' => $prefixFirst,
'KEYS' => $prefixFirst,
'RENAME' => $prefixAll,
'RENAMENX' => $prefixAll,
'EXPIRE' => $prefixFirst,
'EXPIREAT' => $prefixFirst,
'TTL' => $prefixFirst,
'MOVE' => $prefixFirst,
'SORT' => $prefixSort,
'DUMP' => $prefixFirst,
'RESTORE' => $prefixFirst,
'SET' => $prefixFirst,
'SETNX' => $prefixFirst,
'MSET' => $prefixInterleaved,
'MSETNX' => $prefixInterleaved,
'GET' => $prefixFirst,
'MGET' => $prefixAll,
'GETSET' => $prefixFirst,
'INCR' => $prefixFirst,
'INCRBY' => $prefixFirst,
'DECR' => $prefixFirst,
'DECRBY' => $prefixFirst,
'RPUSH' => $prefixFirst,
'LPUSH' => $prefixFirst,
'LLEN' => $prefixFirst,
'LRANGE' => $prefixFirst,
'LTRIM' => $prefixFirst,
'LINDEX' => $prefixFirst,
'LSET' => $prefixFirst,
'LREM' => $prefixFirst,
'LPOP' => $prefixFirst,
'RPOP' => $prefixFirst,
'RPOPLPUSH' => $prefixAll,
'SADD' => $prefixFirst,
'SREM' => $prefixFirst,
'SPOP' => $prefixFirst,
'SMOVE' => $prefixSkipLast,
'SCARD' => $prefixFirst,
'SISMEMBER' => $prefixFirst,
'SINTER' => $prefixAll,
'SINTERSTORE' => $prefixAll,
'SUNION' => $prefixAll,
'SUNIONSTORE' => $prefixAll,
'SDIFF' => $prefixAll,
'SDIFFSTORE' => $prefixAll,
'SMEMBERS' => $prefixFirst,
'SRANDMEMBER' => $prefixFirst,
'ZADD' => $prefixFirst,
'ZINCRBY' => $prefixFirst,
'ZREM' => $prefixFirst,
'ZRANGE' => $prefixFirst,
'ZREVRANGE' => $prefixFirst,
'ZRANGEBYSCORE' => $prefixFirst,
'ZCARD' => $prefixFirst,
'ZSCORE' => $prefixFirst,
'ZREMRANGEBYSCORE' => $prefixFirst,
/* ---------------- Redis 2.0 ---------------- */
'SETEX' => 'static::first',
'APPEND' => 'static::first',
'SUBSTR' => 'static::first',
'BLPOP' => 'static::skipLast',
'BRPOP' => 'static::skipLast',
'ZUNIONSTORE' => 'static::zsetStore',
'ZINTERSTORE' => 'static::zsetStore',
'ZCOUNT' => 'static::first',
'ZRANK' => 'static::first',
'ZREVRANK' => 'static::first',
'ZREMRANGEBYRANK' => 'static::first',
'HSET' => 'static::first',
'HSETNX' => 'static::first',
'HMSET' => 'static::first',
'HINCRBY' => 'static::first',
'HGET' => 'static::first',
'HMGET' => 'static::first',
'HDEL' => 'static::first',
'HEXISTS' => 'static::first',
'HLEN' => 'static::first',
'HKEYS' => 'static::first',
'HVALS' => 'static::first',
'HGETALL' => 'static::first',
'SUBSCRIBE' => 'static::all',
'UNSUBSCRIBE' => 'static::all',
'PSUBSCRIBE' => 'static::all',
'PUNSUBSCRIBE' => 'static::all',
'PUBLISH' => 'static::first',
'SETEX' => $prefixFirst,
'APPEND' => $prefixFirst,
'SUBSTR' => $prefixFirst,
'BLPOP' => $prefixSkipLast,
'BRPOP' => $prefixSkipLast,
'ZUNIONSTORE' => $prefixZsetStore,
'ZINTERSTORE' => $prefixZsetStore,
'ZCOUNT' => $prefixFirst,
'ZRANK' => $prefixFirst,
'ZREVRANK' => $prefixFirst,
'ZREMRANGEBYRANK' => $prefixFirst,
'HSET' => $prefixFirst,
'HSETNX' => $prefixFirst,
'HMSET' => $prefixFirst,
'HINCRBY' => $prefixFirst,
'HGET' => $prefixFirst,
'HMGET' => $prefixFirst,
'HDEL' => $prefixFirst,
'HEXISTS' => $prefixFirst,
'HLEN' => $prefixFirst,
'HKEYS' => $prefixFirst,
'HVALS' => $prefixFirst,
'HGETALL' => $prefixFirst,
'SUBSCRIBE' => $prefixAll,
'UNSUBSCRIBE' => $prefixAll,
'PSUBSCRIBE' => $prefixAll,
'PUNSUBSCRIBE' => $prefixAll,
'PUBLISH' => $prefixFirst,
/* ---------------- Redis 2.2 ---------------- */
'PERSIST' => 'static::first',
'STRLEN' => 'static::first',
'SETRANGE' => 'static::first',
'GETRANGE' => 'static::first',
'SETBIT' => 'static::first',
'GETBIT' => 'static::first',
'RPUSHX' => 'static::first',
'LPUSHX' => 'static::first',
'LINSERT' => 'static::first',
'BRPOPLPUSH' => 'static::skipLast',
'ZREVRANGEBYSCORE' => 'static::first',
'WATCH' => 'static::all',
'PERSIST' => $prefixFirst,
'STRLEN' => $prefixFirst,
'SETRANGE' => $prefixFirst,
'GETRANGE' => $prefixFirst,
'SETBIT' => $prefixFirst,
'GETBIT' => $prefixFirst,
'RPUSHX' => $prefixFirst,
'LPUSHX' => $prefixFirst,
'LINSERT' => $prefixFirst,
'BRPOPLPUSH' => $prefixSkipLast,
'ZREVRANGEBYSCORE' => $prefixFirst,
'WATCH' => $prefixAll,
/* ---------------- Redis 2.6 ---------------- */
'PTTL' => 'static::first',
'PEXPIRE' => 'static::first',
'PEXPIREAT' => 'static::first',
'PSETEX' => 'static::first',
'INCRBYFLOAT' => 'static::first',
'BITOP' => 'static::skipFirst',
'BITCOUNT' => 'static::first',
'HINCRBYFLOAT' => 'static::first',
'EVAL' => 'static::evalKeys',
'EVALSHA' => 'static::evalKeys',
'MIGRATE' => 'static::migrate',
'PTTL' => $prefixFirst,
'PEXPIRE' => $prefixFirst,
'PEXPIREAT' => $prefixFirst,
'PSETEX' => $prefixFirst,
'INCRBYFLOAT' => $prefixFirst,
'BITOP' => $prefixSkipFirst,
'BITCOUNT' => $prefixFirst,
'HINCRBYFLOAT' => $prefixFirst,
'EVAL' => $prefixEvalKeys,
'EVALSHA' => $prefixEvalKeys,
'MIGRATE' => $prefixMigrate,
/* ---------------- Redis 2.8 ---------------- */
'SSCAN' => 'static::first',
'ZSCAN' => 'static::first',
'HSCAN' => 'static::first',
'PFADD' => 'static::first',
'PFCOUNT' => 'static::all',
'PFMERGE' => 'static::all',
'ZLEXCOUNT' => 'static::first',
'ZRANGEBYLEX' => 'static::first',
'ZREMRANGEBYLEX' => 'static::first',
'ZREVRANGEBYLEX' => 'static::first',
'BITPOS' => 'static::first',
'SSCAN' => $prefixFirst,
'ZSCAN' => $prefixFirst,
'HSCAN' => $prefixFirst,
'PFADD' => $prefixFirst,
'PFCOUNT' => $prefixAll,
'PFMERGE' => $prefixAll,
'ZLEXCOUNT' => $prefixFirst,
'ZRANGEBYLEX' => $prefixFirst,
'ZREMRANGEBYLEX' => $prefixFirst,
'ZREVRANGEBYLEX' => $prefixFirst,
'BITPOS' => $prefixFirst,
/* ---------------- Redis 3.2 ---------------- */
'HSTRLEN' => 'static::first',
'BITFIELD' => 'static::first',
'GEOADD' => 'static::first',
'GEOHASH' => 'static::first',
'GEOPOS' => 'static::first',
'GEODIST' => 'static::first',
'GEORADIUS' => 'static::georadius',
'GEORADIUSBYMEMBER' => 'static::georadius',
);
'HSTRLEN' => $prefixFirst,
'BITFIELD' => $prefixFirst,
'GEOADD' => $prefixFirst,
'GEOHASH' => $prefixFirst,
'GEOPOS' => $prefixFirst,
'GEODIST' => $prefixFirst,
'GEORADIUS' => $prefixGeoradius,
'GEORADIUSBYMEMBER' => $prefixGeoradius,
];
}
/**
@@ -197,7 +209,7 @@ class KeyPrefixProcessor implements ProcessorInterface
if ($command instanceof PrefixableCommandInterface) {
$command->prefixKeys($this->prefix);
} elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) {
call_user_func($this->commands[$commandID], $command, $this->prefix);
$this->commands[$commandID]($command, $this->prefix);
}
}
+6 -3
View File
@@ -71,7 +71,7 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* Returns an iterator over the list of command processor in the chain.
*
* @return \ArrayIterator
* @return \Traversable<int, ProcessorInterface>
*/
public function getIterator()
{
@@ -91,6 +91,7 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function offsetExists($index)
{
return isset($this->processors[$index]);
@@ -99,6 +100,7 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function offsetGet($index)
{
return $this->processors[$index];
@@ -107,12 +109,12 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function offsetSet($index, $processor)
{
if (!$processor instanceof ProcessorInterface) {
throw new \InvalidArgumentException(
'A processor chain accepts only instances of '.
"'Predis\Command\Processor\ProcessorInterface'."
'Processor chain accepts only instances of `Predis\Command\Processor\ProcessorInterface`'
);
}
@@ -122,6 +124,7 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function offsetUnset($index)
{
unset($this->processors[$index]);
+17 -22
View File
@@ -12,52 +12,47 @@
namespace Predis\Command;
/**
* Class for generic "anonymous" Redis commands.
* Class representing a generic Redis command.
*
* This command class does not filter input arguments or parse responses, but
* can be used to leverage the standard Predis API to execute any command simply
* by providing the needed arguments following the command signature as defined
* by Redis in its documentation.
* Arguments and responses for these commands are not normalized and they follow
* what is defined by the Redis documentation.
*
* Raw commands can be useful when implementing higher level abstractions on top
* of Predis\Client or managing internals like Redis Sentinel or Cluster as they
* are not potentially subject to hijacking from third party libraries when they
* override command handlers for standard Redis commands.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class RawCommand implements CommandInterface
final class RawCommand implements CommandInterface
{
private $slot;
private $commandID;
private $arguments;
/**
* @param array $arguments Command ID and its arguments.
*
* @throws \InvalidArgumentException
* @param string $commandID Command ID
* @param array $arguments Command arguments
*/
public function __construct(array $arguments)
public function __construct($commandID, array $arguments = array())
{
if (!$arguments) {
throw new \InvalidArgumentException(
'The arguments array must contain at least the command ID.'
);
}
$this->commandID = strtoupper(array_shift($arguments));
$this->arguments = $arguments;
$this->commandID = strtoupper($commandID);
$this->setArguments($arguments);
}
/**
* Creates a new raw command using a variadic method.
*
* @param string $commandID Redis command ID.
* @param string ... Arguments list for the command.
* @param string $commandID Redis command ID
* @param string ... Arguments list for the command
*
* @return CommandInterface
*/
public static function create($commandID /* [ $arg, ... */)
{
$arguments = func_get_args();
$command = new self($arguments);
return $command;
return new static(array_shift($arguments), $arguments);
}
/**
+44
View File
@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Command factory creating raw command instances out of command IDs.
*
* Any command ID will produce a command instance even for unknown commands that
* are not implemented by Redis (the server will return a "-ERR unknown command"
* error responses).
*
* When using this factory the client does not process arguments before sending
* commands to Redis and server responses are not further processed before being
* returned to the caller.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class RawFactory implements FactoryInterface
{
/**
* {@inheritdoc}
*/
public function supports(string ...$commandIDs): bool
{
return true;
}
/**
* {@inheritdoc}
*/
public function create(string $commandID, array $arguments = []): CommandInterface
{
return new RawCommand($commandID, $arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/append
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringAppend extends Command
class APPEND extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/auth
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionAuth extends Command
class AUTH extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/bgrewriteaof
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerBackgroundRewriteAOF extends Command
class BGREWRITEAOF extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/bgsave
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerBackgroundSave extends Command
class BGSAVE extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/bitcount
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringBitCount extends Command
class BITCOUNT extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/bitfield
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringBitField extends Command
class BITFIELD extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/bitop
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringBitOp extends Command
class BITOP extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,7 +31,7 @@ class StringBitOp extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
if (count($arguments) === 3 && is_array($arguments[2])) {
list($operation, $destination) = $arguments;
@@ -37,6 +39,6 @@ class StringBitOp extends Command
array_unshift($arguments, $operation, $destination);
}
return $arguments;
parent::setArguments($arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/bitpos
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringBitPos extends Command
class BITPOS extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/blpop
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopFirstBlocking extends Command
class BLPOP extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,13 +31,13 @@ class ListPopFirstBlocking extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[0])) {
list($arguments, $timeout) = $arguments;
array_push($arguments, $timeout);
}
return $arguments;
parent::setArguments($arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/brpop
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLastBlocking extends ListPopFirstBlocking
class BRPOP extends RedisCommand
{
/**
* {@inheritdoc}
@@ -25,4 +27,17 @@ class ListPopLastBlocking extends ListPopFirstBlocking
{
return 'BRPOP';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[0])) {
list($arguments, $timeout) = $arguments;
array_push($arguments, $timeout);
}
parent::setArguments($arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/brpoplpush
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLastPushHeadBlocking extends Command
class BRPOPLPUSH extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,7 +9,9 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/client-list
@@ -19,7 +21,7 @@ namespace Predis\Command;
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerClient extends Command
class CLIENT extends RedisCommand
{
/**
* {@inheritdoc}
@@ -44,7 +46,7 @@ class ServerClient extends Command
case 'SETNAME':
default:
return $data;
}
} // @codeCoverageIgnore
}
/**
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as BaseCommand;
/**
* @link http://redis.io/commands/command
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerCommand extends Command
class COMMAND extends BaseCommand
{
/**
* {@inheritdoc}
@@ -9,7 +9,9 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/config-set
@@ -19,7 +21,7 @@ namespace Predis\Command;
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerConfig extends Command
class CONFIG extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/dbsize
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerDatabaseSize extends Command
class DBSIZE extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/decr
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringDecrement extends Command
class DECR extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/decrby
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringDecrementBy extends Command
class DECRBY extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/del
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyDelete extends Command
class DEL extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,8 +31,10 @@ class KeyDelete extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
return self::normalizeArguments($arguments);
$arguments = self::normalizeArguments($arguments);
parent::setArguments($arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/discard
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class TransactionDiscard extends Command
class DISCARD extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/dump
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyDump extends Command
class DUMP extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/echo
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionEcho extends Command
class ECHO_ extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
/**
* @link http://redis.io/commands/evalsha
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerEvalSHA extends ServerEval
class EVALSHA extends EVAL_
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/eval
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerEval extends Command
class EVAL_ extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/exec
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class TransactionExec extends Command
class EXEC extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/exists
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExists extends Command
class EXISTS extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/expire
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExpire extends Command
class EXPIRE extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/expireat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExpireAt extends Command
class EXPIREAT extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/flushall
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerFlushAll extends Command
class FLUSHALL extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/flushdb
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerFlushDatabase extends Command
class FLUSHDB extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/geoadd
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoAdd extends Command
class GEOADD extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,7 +31,7 @@ class GeospatialGeoAdd extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
foreach (array_pop($arguments) as $item) {
@@ -37,6 +39,6 @@ class GeospatialGeoAdd extends Command
}
}
return $arguments;
parent::setArguments($arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/geodist
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoDist extends Command
class GEODIST extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/geohash
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoHash extends Command
class GEOHASH extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,13 +31,13 @@ class GeospatialGeoHash extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$members = array_pop($arguments);
$arguments = array_merge($arguments, $members);
}
return $arguments;
parent::setArguments($arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/geopos
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoPos extends Command
class GEOPOS extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,13 +31,13 @@ class GeospatialGeoPos extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$members = array_pop($arguments);
$arguments = array_merge($arguments, $members);
}
return $arguments;
parent::setArguments($arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/georadius
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoRadius extends Command
class GEORADIUS extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,7 +31,7 @@ class GeospatialGeoRadius extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
if ($arguments && is_array(end($arguments))) {
$options = array_change_key_case(array_pop($arguments), CASE_UPPER);
@@ -66,6 +68,6 @@ class GeospatialGeoRadius extends Command
}
}
return $arguments;
parent::setArguments($arguments);
}
}
@@ -9,14 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
/**
* @link http://redis.io/commands/georadiusbymember
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoRadiusByMember extends GeospatialGeoRadius
class GEORADIUSBYMEMBER extends GEORADIUS
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/get
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringGet extends Command
class GET extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/getbit
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringGetBit extends Command
class GETBIT extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/getrange
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringGetRange extends Command
class GETRANGE extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/getset
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringGetSet extends Command
class GETSET extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hdel
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashDelete extends Command
class HDEL extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,8 +31,10 @@ class HashDelete extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
return self::normalizeVariadic($arguments);
$arguments = self::normalizeVariadic($arguments);
parent::setArguments($arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hexists
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashExists extends Command
class HEXISTS extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hget
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGet extends Command
class HGET extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hgetall
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGetAll extends Command
class HGETALL extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hincrby
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashIncrementBy extends Command
class HINCRBY extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hincrbyfloat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashIncrementByFloat extends Command
class HINCRBYFLOAT extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hkeys
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashKeys extends Command
class HKEYS extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hlen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashLength extends Command
class HLEN extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hmget
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGetMultiple extends Command
class HMGET extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,8 +31,10 @@ class HashGetMultiple extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
return self::normalizeVariadic($arguments);
$arguments = self::normalizeVariadic($arguments);
parent::setArguments($arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hmset
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSetMultiple extends Command
class HMSET extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,7 +31,7 @@ class HashSetMultiple extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$flattenedKVs = array($arguments[0]);
@@ -40,9 +42,9 @@ class HashSetMultiple extends Command
$flattenedKVs[] = $v;
}
return $flattenedKVs;
$arguments = $flattenedKVs;
}
return $arguments;
parent::setArguments($arguments);
}
}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hscan
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashScan extends Command
class HSCAN extends RedisCommand
{
/**
* {@inheritdoc}
@@ -29,14 +31,14 @@ class HashScan extends Command
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
public function setArguments(array $arguments)
{
if (count($arguments) === 3 && is_array($arguments[2])) {
$options = $this->prepareOptions(array_pop($arguments));
$arguments = array_merge($arguments, $options);
}
return $arguments;
parent::setArguments($arguments);
}
/**
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hset
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSet extends Command
class HSET extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hsetnx
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSetPreserve extends Command
class HSETNX extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hstrlen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashStringLength extends Command
class HSTRLEN extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/hvals
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashValues extends Command
class HVALS extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/incr
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringIncrement extends Command
class INCR extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/incrby
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringIncrementBy extends Command
class INCRBY extends RedisCommand
{
/**
* {@inheritdoc}
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/incrbyfloat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class StringIncrementByFloat extends Command
class INCRBYFLOAT extends RedisCommand
{
/**
* {@inheritdoc}

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