Compare commits

...

45 Commits

Author SHA1 Message Date
Nicolas Grekas deee2b6d60 Fix deprecation when using "static" in callables (#1403) 2023-09-19 09:11:21 -07:00
Till Krüss bb8cce7bcf adjust travis config 2023-01-10 08:48:39 -08:00
Stefano Borghi dc14604f3e Check resource before reading (#803) 2022-09-20 09:13:12 -07:00
Stefano Borghi b000013f22 check resource before writing (#799) 2022-09-19 12:27:07 -07:00
angeljqv d6da14daa8 Don't export changeling and contribution notes 2022-04-05 09:13:30 -07:00
Andrii Dembitskyi acb2460f4c PHP 8.1 and symfony/error handler deprecations (#748)
* Be sure that position always have type integer

* Annotate properly return types for SPL interfaces implementation

It is allows avoiding deprecation messages from `symfony/error-handler` like a '[info] User Deprecated: Method "Iterator::rewind()" might add "void" as a native return type declaration in the future. Do the same in implementation "Predis\Collection\Iterator\CursorBasedIterator" now to avoid errors or add an explicit @return annotation to suppress this message.'

Fixed via `symfony/error-handler` patch script: ` SYMFONY_PATCH_TYPE_DECLARATIONS="force=phpdoc&php=5.3" ./vendor/bin/patch-type-declarations` and some additional manual work

Some details about why this is necessary and how checks with trigger deprecations works: https://wouterj.nl/2021/09/symfony-6-native-typing
2022-03-15 10:50:51 -07:00
Karol Hrusza 213f00042f Add clear method to pipeline (#749)
* Add clear method to pipeline

Ability to clean pipeline and responses when reusing pipelines.
Allows pipeline to be executed using chunks without out-of-control
increase in memory usage.

* spacing

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-03-11 08:48:10 -08:00
Julian Haupt ab0c46332c throw exception if connection->read() returns an error object in cons… (#746)
* throw exception if connection->read() returns an error object in consumer

* Add test case for Consumer throwing a ServerException when no subscription could be established

* spacing

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-02-17 10:13:02 -08:00
Ondřej Ešler ed19ec3483 #742 create new context for socket connection (#743)
Co-authored-by: Ondřej Ešler <ondrej.esler@peoplepath.com>
2022-02-04 08:41:14 -08:00
fluffycondor 90496e76b5 Fix wrong return iterator value type: implementation returns \stdClass but it's typehinted as array (#740) 2022-01-26 09:22:03 -08:00
Till Krüss 4231c35e09 back to dev 2022-01-05 09:49:10 -08:00
Till Krüss a2fb02d738 bump version to 1.1.10 stable 2022-01-05 09:46:08 -08:00
Pim Jansen ca1f398f7c Fix for changing return types for PHP8.1 (#730)
Co-authored-by: Pim Jansen <pjansen@senet.nl>
2021-12-18 11:23:39 -08:00
kgasienica 5e07be785a bugfix/cannot-use-object-of-type-error-as-array-722 (#724)
Co-authored-by: Karol Gąsienica-Fronek <karol.gasienica-fronek@lppsa.com>
2021-12-01 10:18:59 -08:00
Till Krüss b3cd02e2ba back to dev 2021-10-05 12:03:21 -07:00
Till Krüss c50c3393bb update changelog 2021-10-05 12:02:38 -07:00
Till Krüss 4aa3014bb0 tag 1.1.9 2021-10-05 11:55:45 -07:00
Dries Vints 92adf32e88 Fix PHP 8.1 strlen breakage (#713)
* Fix PHP 8.1 strlen breakage

This adds a fallback to an empty string if the argument is `null` for some reason.

* Update StreamConnection.php

* use `strval()`

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2021-09-30 09:09:37 -07:00
Till Krüss c4560304cf back to dev version 2021-09-29 10:50:22 -07:00
Till Krüss cf5c118a07 tag 1.1.8
tests pass locally PHP 8.1.0RC4-dev
2021-09-29 10:48:39 -07:00
Till Krüss bb9a670c83 add php 8.1 patches for phpunit 2021-09-29 10:43:13 -07:00
Till Krüss 3e56e880e8 nope; and nightly is php 8.0 🤦‍♂️ 2021-09-29 10:06:50 -07:00
Till Krüss 1c71a4e36e does travis do 8.1 yet?
seemingly no docs on this...
2021-09-29 10:02:37 -07:00
Vadim Borodavko 5004f5d6fc Add PHP 8.1 support (#712)
* Add PHP 8.1 support

* Update AbortedMultiExecException.php

* Update CommunicationException.php

* Update ServerSentinel.php

* Update CursorBasedIterator.php

* Update ReplicationStrategy.php

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2021-09-12 15:07:16 -07:00
John Paul E. Balandan, CPA e616f85679 Add ReturnTypeWillChange attribute to CursorBasedIterator (#708) 2021-08-24 10:25:19 -07:00
Alexander M. Turek 9cd093be24 Add ReturnTypeWillChange (#706) 2021-08-05 11:02:38 -07:00
Nick Doulgeridis 305d167b08 Prevent PHP warning
https://github.com/predis/predis/issues/691
2021-05-12 08:29:35 -07:00
Till Krüss 3271e1edcb back to dev 2021-04-04 12:51:10 -07:00
Till Krüss b240daa106 tag v1.1.7 2021-04-04 12:34:46 -07:00
Till Krüss 8fee6ce8c3 Fix PHP 5.3 test and PHP 8 test runs
For some reason Xdebug causes a seg fault under PHP 8 on Travis CI.
2021-04-04 12:33:59 -07:00
Till Krüss 35dd44b222 Merge pull request #686 from remicollet/issue-redis62
fix test expectation for redis 6.2
2021-04-04 10:36:02 -07:00
Till Krüss 545177f09f Merge pull request #685 from BrianMwit/patch-1
Fix misleading argument name in SentinelReplication
2021-04-04 10:34:53 -07:00
Remi Collet 2f9337cb9b fix test expectation for redis 6.2 2021-03-18 11:08:37 +01:00
Pitchaya Boonsarngsuk 437d80a7a3 Fix misleading argument name in SentinelReplication
$this->retryWait is in milliseconds. It's setter misleadingly document it as seconds.
Fixing by renaming the argument variable name without altering the functionality.
2021-03-17 03:41:03 +00:00
Till Krüss 3a14a39d92 Merge pull request #678 from nicolas-grekas/ret-type
Add some return types
2021-02-09 10:09:19 -08:00
Till Krüss 5c9457cc2e Merge pull request #673 from roborourke/apply-patches-on-update
Apply patches on initial install/update
2021-02-09 09:44:11 -08:00
Nicolas Grekas c551fc21f9 Add some return types 2021-02-04 12:06:26 +01:00
Robert O'Rourke 0d84606723 Switch to old array style syntax to support PHP 5.3 2020-11-30 11:07:54 +00:00
Robert O'Rourke c021e380d6 Apply patches on initial install/update
The patches to get PHPUnit working are causing problems for other projects that use both `cweagans/composer-patch-installers` and `predis/predis` along with PHPUnit.

This change moves the patch application to a simple PHP script run on the `post-update-cmd` composer event and removes the dependency on the patch installers package.
2020-11-27 17:24:00 +00:00
Daniele Alessandri 5f4b87080f Tweak tests. 2020-09-20 17:16:49 +02:00
Daniele Alessandri 5ae4ac6b49 Clear pending responses in reader buffer on disconnect.
When using the phpiredis-based connection backends, failed pipelines led
to spurious responses returned after reconnecting to Redis because the
underlying reader was not properly reset by discarding buffered replies
after disconnecting.

Fixes a couple of issues reported in #363.
2020-09-20 16:58:18 +02:00
Daniele Alessandri 521ac8f0eb Fix PhpiredisSocketConnection for PHP 8.
Removed is_resource() check as socket_create() now returns an object.
2020-09-20 15:27:35 +02:00
Daniele Alessandri fbbef2a0a2 Back to development. 2020-09-11 21:20:11 +02:00
Daniele Alessandri 9930e933c6 Update CHANGELOG and bump VERSION. 2020-09-11 21:18:05 +02:00
Daniele Alessandri 16957f3b39 Revert support for redis-sentinel authentication.
Authentication for sentinels was implemented in v1.1.5 (commit 2e76410)
but ended up being bugged (see ISSUE #658). This is now postponed as it
requires a more thorough investigation.
2020-09-11 21:10:40 +02:00
40 changed files with 428 additions and 89 deletions
+2
View File
@@ -11,3 +11,5 @@
/.travis.yml export-ignore
/phpunit.xml.dist export-ignore
/phpunit.xml.travisci export-ignore
/CHANGELOG.md export-ignore
/CONTRIBUTING.md export-ignore
+9 -8
View File
@@ -1,18 +1,21 @@
language: php
sudo: false
branches:
except:
- v0.5
- v0.6
- v0.6-PHP_5.2
- documentation
- main
- v2.0
before_install:
- docker run -d --rm -p 127.0.0.1:6379:6379 redis:3
before_script:
- phpenv config-rm xdebug.ini
- 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:
@@ -30,6 +33,4 @@ matrix:
- php: 7.2
- php: 7.3
- php: 7.4
- php: nightly # PHP 8.0.0-dev
allow_failures:
- php: nightly
- php: 8.0
+41 -2
View File
@@ -1,7 +1,46 @@
v1.1.10 (2022-01-05)
================================================================================
- __FIX__: Avoid PHP 8.1 deprecation notices in `Session/Handler`
- __FIX__: Fixed "Cannot use object of type Predis\Response\Error as array"
error in `Connection/Aggregate/SentinelReplication`
v1.1.9 (2021-10-05)
================================================================================
- __FIX__: Fixed PHP 8.1 compatibility in `StreamConnection`
v1.1.8 (2021-09-29)
================================================================================
- __FIX__: Ensure compatibility with PHP 8.1.
v1.1.7 (2021-04-04)
================================================================================
- __FIX__: with the phpiredis-based connection backends, failed pipelines led to
spurious responses returned after reconnecting to Redis because the underlying
reader was not properly reset by discarding buffered replies after disconnecting
(ISSUE #363).
- __FIX__: removed `cweagans/composer-patches` dev dependency and apply patches
using `post-update-cmd` script.
v1.1.6 (2020-09-11)
================================================================================
- __FIX__: reverted support for sentinels authentication implemented in v1.1.5
as it was bugged (see ISSUE #658), sorry for the trouble. This is now postponed
as it requires a more thorough investigation.
v1.1.5 (2020-09-10)
================================================================================
- __FIX__: authentication for sentinels is now supported, previously it was not
- __FIX__:~~authentication for sentinels is now supported, previously it was not
possible to specify a `password` for sentinels as its value was stripped during
initialization because sentinels did not support authentication until Redis 5.
**Please note** that with the current implementation each sentinel must have
@@ -9,7 +48,7 @@ its own `password` parameter set in the parameters list despite this password is
the same for all sentinels (read how `requirepass` works on the Redis docs). In
this case you should avoid using the global `parameters` client option used to
set default parameters for every connection created by Predis as this would end
up using the same password even when connecting to actual Redis nodes.
up using the same password even when connecting to actual Redis nodes.~~
- __FIX__: 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.
+1 -1
View File
@@ -1 +1 @@
1.1.6
1.1.11-dev
+4 -14
View File
@@ -31,8 +31,7 @@
"php": ">=5.3.9"
},
"require-dev": {
"phpunit/phpunit": "~4.8",
"cweagans/composer-patches": "^1.6"
"phpunit/phpunit": "~4.8"
},
"suggest": {
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
@@ -43,16 +42,7 @@
"Predis\\": "src/"
}
},
"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"
}
}
}
"scripts": {
"post-update-cmd": "@php -f tests/apply-patches.php"
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ name = "Predis"
desc = "Flexible and feature-complete Redis client for PHP and HHVM"
homepage = "http://github.com/nrk/predis"
license = "MIT"
version = "1.1.6"
version = "1.1.11"
stability = "devel"
channel = "pear.nrk.io"
-4
View File
@@ -36,10 +36,6 @@
</whitelist>
</filter>
<logging>
<log type="coverage-text" target="php://stdout" />
</logging>
<php>
<!-- Redis -->
<const name="REDIS_SERVER_VERSION" value="3.2" />
+3 -2
View File
@@ -40,7 +40,7 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
*/
class Client implements ClientInterface, \IteratorAggregate
{
const VERSION = '1.1.6-dev';
const VERSION = '1.1.11-dev';
protected $connection;
protected $options;
@@ -527,8 +527,9 @@ class Client implements ClientInterface, \IteratorAggregate
}
/**
* {@inheritdoc}
* @return \Traversable<string, static>
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
$clients = array();
@@ -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;
}
@@ -138,7 +138,9 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->reset();
@@ -147,7 +149,9 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
* @return mixed
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -155,7 +159,9 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
* @return int
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -163,7 +169,9 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function next()
{
tryFetch: {
@@ -183,7 +191,9 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+10
View File
@@ -127,7 +127,9 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->reset();
@@ -136,7 +138,9 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
* @return mixed
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -144,7 +148,9 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
* @return int
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -152,7 +158,9 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function next()
{
if (!$this->elements && $this->fetchmore) {
@@ -168,7 +176,9 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+1 -1
View File
@@ -104,7 +104,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];
}
+7 -1
View File
@@ -197,7 +197,13 @@ 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);
$callable = $this->commands[$commandID];
if (is_string($callable) && 0 === strpos($callable, 'static::')) {
$callable = substr_replace($callable, get_class($this), 0, 6);
}
call_user_func($callable, $command, $this->prefix);
}
}
+9 -2
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()
{
@@ -89,8 +89,9 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
}
/**
* {@inheritdoc}
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($index)
{
return isset($this->processors[$index]);
@@ -98,7 +99,9 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
* @return ProcessorInterface|null
*/
#[\ReturnTypeWillChange]
public function offsetGet($index)
{
return $this->processors[$index];
@@ -106,7 +109,9 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetSet($index, $processor)
{
if (!$processor instanceof ProcessorInterface) {
@@ -121,7 +126,9 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetUnset($index)
{
unset($this->processors[$index]);
+4 -1
View File
@@ -31,7 +31,10 @@ class ServerSentinel extends Command
*/
public function parseResponse($data)
{
switch (strtolower($this->getArgument(0))) {
$argument = $this->getArgument(0);
$argument = is_null($argument) ? null : strtolower($argument);
switch ($argument) {
case 'masters':
case 'slaves':
return self::processMastersOrSlaves($data);
+6 -1
View File
@@ -34,7 +34,12 @@ abstract class CommunicationException extends PredisException
$code = null,
\Exception $innerException = null
) {
parent::__construct($message, $code, $innerException);
parent::__construct(
is_null($message) ? '' : $message,
is_null($code) ? 0 : $code,
$innerException
);
$this->connection = $connection;
}
+4 -2
View File
@@ -176,16 +176,18 @@ class PredisCluster implements ClusterInterface, \IteratorAggregate, \Countable
}
/**
* {@inheritdoc}
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return count($this->pool);
}
/**
* {@inheritdoc}
* @return \Traversable<string|int, NodeConnectionInterface>
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this->pool);
+4 -2
View File
@@ -600,16 +600,18 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
}
/**
* {@inheritdoc}
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return count($this->pool);
}
/**
* {@inheritdoc}
* @return \Traversable<int, NodeConnectionInterface>
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
if ($this->useClusterSlots) {
@@ -20,6 +20,7 @@ use Predis\Connection\NodeConnectionInterface;
use Predis\Connection\Parameters;
use Predis\Replication\ReplicationStrategy;
use Predis\Replication\RoleException;
use Predis\Response\Error;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ServerException;
@@ -146,14 +147,14 @@ class SentinelReplication implements ReplicationInterface
}
/**
* Sets the time to wait (in seconds) before fetching a new configuration
* Sets the time to wait (in milliseconds) before fetching a new configuration
* from one of the sentinels.
*
* @param float $seconds Time to wait before the next attempt.
* @param float $milliseconds Time to wait before the next attempt.
*/
public function setRetryWait($seconds)
public function setRetryWait($milliseconds)
{
$this->retryWait = (float) $seconds;
$this->retryWait = (float) $milliseconds;
}
/**
@@ -239,12 +240,15 @@ class SentinelReplication implements ReplicationInterface
}
if (is_array($parameters)) {
// Password authentication is fine now that Redis Sentinel supports
// password-protected sentinel instances, but we must explicitly set
// "database" and "username" to NULL so that no augmented AUTH (ACL)
// and SELECT command are sent by accident to the sentinels.
// NOTE: sentinels do not accept AUTH and SELECT commands so we must
// explicitly set them to NULL to avoid problems when using default
// parameters set via client options. Actually AUTH is supported for
// sentinels starting with Redis 5 but we have to differentiate from
// sentinels passwords and nodes passwords, this will be implemented
// in a later release.
$parameters['database'] = null;
$parameters['username'] = null;
$parameters['password'] = null;
if (!isset($parameters['timeout'])) {
$parameters['timeout'] = $this->sentinelTimeout;
@@ -520,13 +524,17 @@ class SentinelReplication implements ReplicationInterface
* @param NodeConnectionInterface $connection Connection to a redis server.
* @param string $role Expected role of the server ("master", "slave" or "sentinel").
*
* @throws RoleException
* @throws RoleException|ConnectionException
*/
protected function assertConnectionRole(NodeConnectionInterface $connection, $role)
{
$role = strtolower($role);
$actualRole = $connection->executeCommand(RawCommand::create('ROLE'));
if ($actualRole instanceof Error) {
throw new ConnectionException($connection, $actualRole->getMessage());
}
if ($role !== $actualRole[0]) {
throw new RoleException($connection, "Expected $role but got $actualRole[0] [$connection]");
}
+5 -5
View File
@@ -66,9 +66,9 @@ class PhpiredisSocketConnection extends AbstractConnection
*/
public function __destruct()
{
phpiredis_reader_destroy($this->reader);
parent::__destruct();
phpiredis_reader_destroy($this->reader);
}
/**
@@ -227,9 +227,7 @@ class PhpiredisSocketConnection extends AbstractConnection
$protocol = SOL_TCP;
}
$socket = @socket_create($domain, SOCK_STREAM, $protocol);
if (!is_resource($socket)) {
if (false === $socket = @socket_create($domain, SOCK_STREAM, $protocol)) {
$this->emitSocketError();
}
@@ -344,7 +342,9 @@ class PhpiredisSocketConnection extends AbstractConnection
public function disconnect()
{
if ($this->isConnected()) {
phpiredis_reader_reset($this->reader);
socket_close($this->getResource());
parent::disconnect();
}
}
+14 -3
View File
@@ -67,9 +67,19 @@ class PhpiredisStreamConnection extends StreamConnection
*/
public function __destruct()
{
phpiredis_reader_destroy($this->reader);
parent::__destruct();
phpiredis_reader_destroy($this->reader);
}
/**
* {@inheritdoc}
*/
public function disconnect()
{
phpiredis_reader_reset($this->reader);
parent::disconnect();
}
/**
@@ -113,8 +123,9 @@ class PhpiredisStreamConnection extends StreamConnection
{
$socket = null;
$timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
$context = stream_context_create();
$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags);
$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags, $context);
if (!$resource) {
$this->onConnectionError(trim($errstr), $errno);
+5 -4
View File
@@ -123,8 +123,9 @@ class StreamConnection extends AbstractConnection
protected function createStreamSocket(ParametersInterface $parameters, $address, $flags)
{
$timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
$context = stream_context_create();
if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags)) {
if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags, $context)) {
$this->onConnectionError(trim($errstr), $errno);
}
@@ -288,7 +289,7 @@ class StreamConnection extends AbstractConnection
$socket = $this->getResource();
while (($length = strlen($buffer)) > 0) {
$written = @fwrite($socket, $buffer);
$written = is_resource($socket) ? @fwrite($socket, $buffer) : false;
if ($length === $written) {
return;
@@ -332,7 +333,7 @@ class StreamConnection extends AbstractConnection
$bytesLeft = ($size += 2);
do {
$chunk = fread($socket, min($bytesLeft, 4096));
$chunk = is_resource($socket) ? fread($socket, min($bytesLeft, 4096)) : false;
if ($chunk === false || $chunk === '') {
$this->onConnectionError('Error while reading bytes from the server.');
@@ -387,7 +388,7 @@ class StreamConnection extends AbstractConnection
$buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n";
foreach ($arguments as $argument) {
$arglen = strlen($argument);
$arglen = strlen(strval($argument));
$buffer .= "\${$arglen}\r\n{$argument}\r\n";
}
+9 -1
View File
@@ -24,7 +24,7 @@ class Consumer implements \Iterator
{
private $client;
private $valid;
private $position;
private $position = 0;
/**
* @param ClientInterface $client Client instance used by the consumer.
@@ -90,7 +90,9 @@ class Consumer implements \Iterator
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function rewind()
{
// NOOP
@@ -101,6 +103,7 @@ class Consumer implements \Iterator
*
* @return object
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->getValue();
@@ -108,7 +111,9 @@ class Consumer implements \Iterator
/**
* {@inheritdoc}
* @return int
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -116,7 +121,9 @@ class Consumer implements \Iterator
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function next()
{
++$this->position;
@@ -127,6 +134,7 @@ class Consumer implements \Iterator
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+13
View File
@@ -225,6 +225,19 @@ class Pipeline implements ClientContextInterface
return $this->responses;
}
/**
* Clear the buffer holding all of the commands and responses.
*
* @return $this
*/
public function clear()
{
$this->responses = array();
$this->pipeline = new \SplQueue();
return $this;
}
/**
* Returns if the pipeline should throw exceptions on server errors.
*
+10 -3
View File
@@ -30,7 +30,7 @@ abstract class AbstractConsumer implements \Iterator
const STATUS_SUBSCRIBED = 2; // 0b0010
const STATUS_PSUBSCRIBED = 4; // 0b0100
private $position = null;
private $position = 0;
private $statusFlags = self::STATUS_VALID;
/**
@@ -150,7 +150,9 @@ abstract class AbstractConsumer implements \Iterator
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function rewind()
{
// NOOP
@@ -160,8 +162,9 @@ abstract class AbstractConsumer implements \Iterator
* Returns the last message payload retrieved from the server and generated
* by one of the active subscriptions.
*
* @return array
* @return \stdClass
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->getValue();
@@ -169,7 +172,9 @@ abstract class AbstractConsumer implements \Iterator
/**
* {@inheritdoc}
* @return int
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -178,6 +183,7 @@ abstract class AbstractConsumer implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function next()
{
if ($this->valid()) {
@@ -192,6 +198,7 @@ abstract class AbstractConsumer implements \Iterator
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
$isValid = $this->isFlagSet(self::STATUS_VALID);
@@ -213,7 +220,7 @@ abstract class AbstractConsumer implements \Iterator
* Waits for a new message from the server generated by one of the active
* subscriptions and returns it when available.
*
* @return array
* @return \stdClass
*/
abstract protected function getValue();
}
+6
View File
@@ -16,6 +16,8 @@ use Predis\ClientInterface;
use Predis\Command\Command;
use Predis\Connection\AggregateConnectionInterface;
use Predis\NotSupportedException;
use Predis\Response\ServerException;
use Predis\Response\Error;
/**
* PUB/SUB consumer abstraction.
@@ -116,6 +118,10 @@ class Consumer extends AbstractConsumer
{
$response = $this->client->getConnection()->read();
if ($response instanceof Error) {
throw new ServerException($response->getMessage());
}
switch ($response[0]) {
case self::SUBSCRIBE:
case self::UNSUBSCRIBE:
+2 -1
View File
@@ -62,7 +62,8 @@ class ReplicationStrategy
}
if (($eval = $id === 'EVAL') || $id === 'EVALSHA') {
$sha1 = $eval ? sha1($command->getArgument(0)) : $command->getArgument(0);
$argument = $command->getArgument(0);
$sha1 = $eval ? sha1(strval($argument)) : $argument;
if (isset($this->readonlySHA1[$sha1])) {
if (true === $readonly = $this->readonlySHA1[$sha1]) {
@@ -33,7 +33,9 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function rewind()
{
// NOOP
@@ -41,7 +43,9 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
/**
* {@inheritdoc}
* @return mixed
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -49,7 +53,9 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
/**
* {@inheritdoc}
* @return int
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -57,7 +63,9 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function next()
{
if (++$this->position < $this->size) {
@@ -67,7 +75,9 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
/**
* {@inheritdoc}
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->position < $this->size;
@@ -82,6 +92,7 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
*
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return $this->size;
+2
View File
@@ -60,7 +60,9 @@ class MultiBulkTuple extends MultiBulk implements \OuterIterator
/**
* {@inheritdoc}
* @return \Iterator
*/
#[\ReturnTypeWillChange]
public function getInnerIterator()
{
return $this->iterator;
+12
View File
@@ -64,7 +64,9 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
* @return bool
*/
#[\ReturnTypeWillChange]
public function open($save_path, $session_id)
{
// NOOP
@@ -73,7 +75,9 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
* @return bool
*/
#[\ReturnTypeWillChange]
public function close()
{
// NOOP
@@ -82,7 +86,9 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
* @return int|bool
*/
#[\ReturnTypeWillChange]
public function gc($maxlifetime)
{
// NOOP
@@ -91,7 +97,9 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
* @return string
*/
#[\ReturnTypeWillChange]
public function read($session_id)
{
if ($data = $this->client->get($session_id)) {
@@ -102,7 +110,9 @@ class Handler implements \SessionHandlerInterface
}
/**
* {@inheritdoc}
* @return bool
*/
#[\ReturnTypeWillChange]
public function write($session_id, $session_data)
{
$this->client->setex($session_id, $this->ttl, $session_data);
@@ -112,7 +122,9 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
* @return bool
*/
#[\ReturnTypeWillChange]
public function destroy($session_id)
{
$this->client->del($session_id);
@@ -29,7 +29,7 @@ class AbortedMultiExecException extends PredisException
*/
public function __construct(MultiExec $transaction, $message, $code = null)
{
parent::__construct($message, $code);
parent::__construct($message, is_null($code) ? 0 : $code);
$this->transaction = $transaction;
}
@@ -83,7 +83,7 @@ class ConnectionSelectTest extends PredisCommandTestCase
/**
* @group connected
* @expectedException \Predis\Response\ServerException
* @expectedExceptionMessage ERR invalid DB index
* @expectedExceptionMessageRegExp /ERR (invalid DB index|value is not)/
*/
public function testThrowsExceptionOnUnexpectedDatabaseName()
{
+1 -1
View File
@@ -82,7 +82,7 @@ class KeyMoveTest extends PredisCommandTestCase
/**
* @group connected
* @expectedException \Predis\Response\ServerException
* @expectedExceptionMessage ERR index out of range
* @expectedExceptionMessageRegEx /ERR.*out of range/
*/
public function testThrowsExceptionOnInvalidDatabases()
{
@@ -66,6 +66,10 @@ class ReplicationOptionTest extends PredisTestCase
*/
public function testDoesNotAcceptValuesThatCanBeInterpretedAsBooleanFalse($value)
{
if (version_compare(PHP_VERSION, '5.4', '<')) {
$this->markTestSkipped('Skip test, because.');
}
$option = new ReplicationOption();
$options = $this->getMock('Predis\Configuration\OptionsInterface');
@@ -36,29 +36,19 @@ class SentinelReplicationTest extends PredisTestCase
/**
* @group disconnected
*/
public function testParametersForSentinelConnectionShouldUsePasswordForAuthentication()
public function testParametersForSentinelConnectionShouldNotUseDatabaseAndUsernameAndPassword()
{
$replication = $this->getReplicationConnection('svc', array(
'tcp://127.0.0.1:5381?alias=sentinel1&password=secret',
'tcp://127.0.0.1:5381?alias=sentinel1&database=1&username=myusername&password=secret',
));
$parameters = $replication->getSentinelConnection()->getParameters()->toArray();
$this->assertArraySubset(array('password' => 'secret'), $parameters);
}
/**
* @group disconnected
*/
public function testParametersForSentinelConnectionShouldNotUseDatabaseAndUsername()
{
$replication = $this->getReplicationConnection('svc', array(
'tcp://127.0.0.1:5381?alias=sentinel1&database=1&username=myusername',
));
$parameters = $replication->getSentinelConnection()->getParameters()->toArray();
$this->assertArraySubset(array('database' => null, 'username' => null), $parameters);
$this->assertArraySubset(array(
'database' => null,
'username' => null,
'password' => null
), $parameters);
}
/**
@@ -76,6 +76,28 @@ class PhpiredisSocketConnectionTest extends PredisConnectionTestCase
// ---- INTEGRATION TESTS --------------------------------------------- //
// ******************************************************************** //
/**
* @group connected
*/
public function testClearsPendingResponsesInReaderBufferOnDisconnect()
{
$profile = $this->getCurrentProfile();
$connection = $this->createConnection();
$cmdECHOBefore = $profile->createCommand('echo', array('BEFORE DISCONNECT'));
$cmdECHOAfter = $profile->createCommand('echo', array('AFTER DISCONNECT'));
$connection->writeRequest($cmdECHOBefore);
$connection->writeRequest($cmdECHOBefore);
$connection->writeRequest($cmdECHOBefore);
$connection->readResponse($cmdECHOBefore);
$connection->disconnect();
$response = $connection->executeCommand($cmdECHOAfter);
$this->assertSame('AFTER DISCONNECT', $response);
}
/**
* @group connected
* @expectedException \Predis\Connection\ConnectionException
@@ -76,6 +76,28 @@ class PhpiredisStreamConnectionTest extends PredisConnectionTestCase
// ---- INTEGRATION TESTS --------------------------------------------- //
// ******************************************************************** //
/**
* @group connected
*/
public function testClearsPendingResponsesInReaderBufferOnDisconnect()
{
$profile = $this->getCurrentProfile();
$connection = $this->createConnection();
$cmdECHOBefore = $profile->createCommand('echo', array('BEFORE DISCONNECT'));
$cmdECHOAfter = $profile->createCommand('echo', array('AFTER DISCONNECT'));
$connection->writeRequest($cmdECHOBefore);
$connection->writeRequest($cmdECHOBefore);
$connection->writeRequest($cmdECHOBefore);
$connection->readResponse($cmdECHOBefore);
$connection->disconnect();
$response = $connection->executeCommand($cmdECHOAfter);
$this->assertSame('AFTER DISCONNECT', $response);
}
/**
* @group connected
* @group slow
+53
View File
@@ -234,6 +234,59 @@ class PipelineTest extends PredisTestCase
$this->assertSame(array('one', 'two', 'three', 'four'), $pipeline->execute());
}
/**
* @group disconnected
*/
public function testClearBuffer()
{
$connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
$connection->expects($this->never())
->method('writeRequest');
$connection->expects($this->never())
->method('readResponse')
->will($this->returnCallback($this->getReadCallback()));
$pipeline = new Pipeline(new Client($connection));
$pipeline->echo('one');
$pipeline->echo('two');
$pipeline->clear();
$this->assertSame(array(), $pipeline->execute());
}
/**
* @group disconnected
*/
public function testClearResponses()
{
$connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
$connection->expects($this->exactly(4))
->method('writeRequest');
$connection->expects($this->exactly(4))
->method('readResponse')
->will($this->returnCallback($this->getReadCallback()));
$pipeline = new Pipeline(new Client($connection));
$pipeline->echo('one');
$pipeline->echo('two');
$this->assertSame(array('one', 'two'), $pipeline->execute());
$pipeline->clear();
$pipeline->echo('three');
$pipeline->echo('four');
$this->assertSame(array('three', 'four'), $pipeline->execute());
$pipeline->clear();
$this->assertSame(array(), $pipeline->execute());
}
/**
* @group disconnected
*/
+28
View File
@@ -426,4 +426,32 @@ class ConsumerTest extends PredisTestCase
exit(0);
}
}
/**
* @group connected
* @expectedException \Predis\Response\ServerException
*/
public function testInvalidSubscriptionThrowsServerException()
{
$parameters = array(
'host' => REDIS_SERVER_HOST,
'port' => REDIS_SERVER_PORT,
'database' => REDIS_SERVER_DBNUM,
'read_write_timeout' => -1, // -1 to set blocking reads
);
$options = array('profile' => REDIS_SERVER_VERSION);
$consumer = new Client($parameters, $options);
$consumer->connect();
$pubsub = $consumer->pubSubLoop();
// calling psubscribe with invalid empty parameter list
$pubsub->psubscribe(array());
foreach ($pubsub as $message) {
// should trigger the ServerException
}
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
/**
* Apply patches to support newer PHP versions.
*/
$patches = array(
'phpunit_mock_objects.patch' => 'phpunit/phpunit-mock-objects',
'phpunit_php7.patch' => 'phpunit/phpunit',
'phpunit_php8.patch' => 'phpunit/phpunit',
'phpunit_php81.patch' => 'phpunit/phpunit',
);
foreach ($patches as $patch => $package) {
chdir(__DIR__.'/../vendor/'.$package);
passthru(sprintf('patch -p1 -f < ../../../tests/%s', $patch));
}
+50
View File
@@ -0,0 +1,50 @@
diff --git a/src/Util/Configuration.php b/src/Util/Configuration.php
index b2f7a7bd0c6..1a1a444d378 100644
--- a/src/Util/Configuration.php
+++ b/src/Util/Configuration.php
@@ -514,22 +514,17 @@ public function handlePHPConfiguration()
foreach (array('var', 'post', 'get', 'cookie', 'server', 'files', 'request') as $array) {
// See https://github.com/sebastianbergmann/phpunit/issues/277
- switch ($array) {
- case 'var':
- $target = &$GLOBALS;
- break;
-
- case 'server':
- $target = &$_SERVER;
- break;
-
- default:
- $target = &$GLOBALS['_' . strtoupper($array)];
- break;
- }
-
foreach ($configuration[$array] as $name => $value) {
- $target[$name] = $value;
+ switch ($array) {
+ case 'var':
+ $GLOBALS[$name] = $value;
+ break;
+ case 'server':
+ $_SERVER[$name] = $value;
+ break;
+ default:
+ $GLOBALS['_' . strtoupper($array)][$name] = $value;
+ }
}
}
diff --git a/src/Util/Getopt.php b/src/Util/Getopt.php
index 370e97d73f8..5bd8553970b 100644
--- a/src/Util/Getopt.php
+++ b/src/Util/Getopt.php
@@ -144,7 +144,7 @@ protected static function parseLongOption($arg, $long_options, &$opts, &$args)
if (substr($long_opt, -1) == '=') {
if (substr($long_opt, -2) != '==') {
- if (!strlen($opt_arg)) {
+ if (null === $opt_arg || strlen($opt_arg) < 1) {
$opt_arg = current($args);
next($args);
if (!($opt_arg)) {