GithubHelp home page GithubHelp logo

cheprasov / php-redis-client Goto Github PK

View Code? Open in Web Editor NEW
126.0 17.0 42.0 726 KB

RedisClient is a fast, fully-functional and user-friendly client for Redis, optimized for performance. RedisClient supports the latest versions of Redis starting from 2.6 to 6.0

License: MIT License

PHP 100.00%
redis php client redis-cluster redis-client php-redis-client

php-redis-client's Introduction

MIT license Latest Stable Version Total Downloads

RedisClient v1.10.0 for PHP >= 5.5

About

RedisClient is a fast, fully-functional and user-friendly client for Redis, optimized for performance. RedisClient supports the latest versions of Redis starting from 2.6 to 6.0

Main features

  • Support Redis versions from 2.6.x to 6.0.x.
  • Support TCP/IP and UNIX sockets.
  • Support PubSub and Monitor functionallity.
  • Support Pipeline and Transactions.
  • Support Redis Cluster.
  • Support RAW commands as arrays ['SET', 'foo', 'bar'].
  • Connections to Redis are established lazily by the client upon the first command.
  • Easy to use with IDE, client has PHPDocs for all supported versions.
  • By default, the client works with the latest stable version of Redis (6.0).
  • The client was tested on the next latest versions of Redis: 6.0.5, 5.0.5, 4.0.14, 3.2.8, 3.0.7, 2.8.24, 2.6.17.
  • Also, the client was tested on PHP 7.4, 7.3, 7.2, 7.1, 7.0, 5.6, 5.5, HHVM.

Redis Commands

Please see list of commands here ./COMMANDS.md

Usage

Config

$config = [
    // Optional. Default = '127.0.0.1:6379'. You can use 'unix:///tmp/redis.sock'
    'server' => '127.0.0.1:6379',

    // Optional. Default = 1
    // The timeout for reading/writing data over the socket
    'timeout' => 2,

    // Optional. Default = null
    // See more here: http://php.net/manual/en/function.stream-socket-client.php
    'connection' => [
        // Optional. Default = ini_get("default_socket_timeout")
        // The timeout only applies while making connecting the socket
        'timeout' => 2,

        // Optional. Default = STREAM_CLIENT_CONNECT
        // Bitmask field which may be set to any combination of connection flags.
        // Currently the select of connection flags is limited to STREAM_CLIENT_CONNECT (default),
        // STREAM_CLIENT_ASYNC_CONNECT and STREAM_CLIENT_PERSISTENT.
        'flags' => STREAM_CLIENT_CONNECT
    ],

    // Optional. Specify version to avoid some unexpected errors.
    'version' => '4.0.10',

    // Optional. Use it only if Redis server requires password (AUTH)
    'password' => 'some-password',

    // Optional. Use it, if you want to select not default db (db != 0) on connect
    'database' => 1,

    // Optional. Array with configs for RedisCluster support
    'cluster' => [
        'enabled' => false,

        // Optional. Default = []. Map of cluster slots and servers
        // array(max_slot => server [, ...])
        // Examples for Cluster with 3 Nodes:
        'clusters' => [
            5460  => '127.0.0.1:7001', // slots from 0 to 5460
            10922 => '127.0.0.1:7002', // slots from 5461 to 10922
            16383 => '127.0.0.1:7003', // slots from 10923 to 16383
        ],

        // Optional. Default = false.
        // Use the param to update cluster slot map below on init RedisClient.
        // RedisClient will execute command CLUSTER SLOTS to get map.
        'init_on_start' => false,

        // Optional. Default = false.
        // If Redis returns error -MOVED then RedisClient will execute
        // command CLUSTER SLOTS to update cluster slot map
        'init_on_error_moved' => true,

        // Optional. Defatult = 0.05 sec. It is timeout before next attempt on TRYAGAIN error.
        'timeout_on_error_tryagain' => 0.25, // sec
    ]
];

Create a new instance of RedisClient

<?php
namespace Examples;

require (dirname(__DIR__).'/vendor/autoload.php');
// or require (dirname(__DIR__).'/src/autoloader.php');

use RedisClient\RedisClient;
use RedisClient\Client\Version\RedisClient2x6;
use RedisClient\ClientFactory;

// Example 1. Create new Instance for Redis version 6.0.x with config via factory
$Redis = ClientFactory::create([
    'server' => '127.0.0.1:6379', // or 'unix:///tmp/redis.sock'
    'timeout' => 2,
    'version' => '6.0'
]);

echo 'RedisClient: '. $Redis->getSupportedVersion() . PHP_EOL; // RedisClient: 2.8


// Example 2. Create new Instance without config. Client will use default config.
$Redis = new RedisClient();
// By default, the client works with the latest stable version of Redis.
echo 'RedisClient: '. $Redis->getSupportedVersion() . PHP_EOL; // RedisClient: 3.2
echo 'Redis: '. $Redis->info('Server')['redis_version'] . PHP_EOL; // Redis: 3.0.3


// Example 3. Create new Instance with config
// By default, the client works with the latest stable version of Redis.
$Redis = new RedisClient([
    'server' => '127.0.0.1:6387', // or 'unix:///tmp/redis.sock'
    'timeout' => 2
]);

echo 'RedisClient: '. $Redis->getSupportedVersion() . PHP_EOL; // RedisClient: 3.2
echo 'Redis: '. $Redis->info('Server')['redis_version'] . PHP_EOL; // Redis: 3.2.0


// Example 4. Create new Instance for Redis version 2.6.x with config
$Redis = new RedisClient2x6([
    'server' => 'tcp://127.0.0.1:6379', // or 'unix:///tmp/redis.sock'
    'timeout' => 2
]);

echo 'RedisClient: '. $Redis->getSupportedVersion() . PHP_EOL; // RedisClient: 2.6

Example

Please, see examples here: https://github.com/cheprasov/php-redis-client/tree/master/examples

Installation

Composer

Download composer:

wget -nc http://getcomposer.org/composer.phar

and add dependency to your project:

php composer.phar require cheprasov/php-redis-client

Running tests

  1. Run Docker container with Redis for tests https://hub.docker.com/r/cheprasov/redis-for-tests/

  2. Run Docker container with Redis Cluster for tests https://hub.docker.com/r/cheprasov/redis-cluster-for-tests/

  3. To run tests type in console:

    ./vendor/bin/phpunit

Something doesn't work

Feel free to fork project, fix bugs and finally request for pull

php-redis-client's People

Contributors

brianfranklin avatar cheprasov avatar nickdnk avatar tcieslar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-redis-client's Issues

DEAD REPOSITORY

Hi Fred,

Thanks a lot.
Yep, unfortunately, I have not time anymore for free maintenance open source project. Sorry.
Please feel free to use another libraries for using redis.

Regards,
Alexander

so we need to clone the repo and maintain it on our own ๐Ÿ˜’

Protocol issue: Syntax error at offset

Dear Alex,

Great job on the redis client!
Today I was connecting to RediSearch and I think I might have found a small issue.
For a case such as this one, where double quotes are used:

->executeRaw(['FT.SEARCH', 'index', '@location:"not far"'])

A following situation happens:

RedisClient\Exception\ErrorResponseException: Syntax error at offset 11 near not in ...

redis monitor is reporting a following command:

"FT.SEARCH" "index" "'@location:\"not far\"'"

However I am not sure if it's escape related, or is it just the double quote character.
It is the recommended syntax:
https://oss.redislabs.com/redisearch/Query_Syntax.html
See "Exact phrases are wrapped in quotes, e.g "hello world"".

When running the same command directly, the syntax issue does not occur:

127.0.0.1:6379> FT.SEARCH index @location:"not far"
1) (integer) 1
2) "15cc84e0485468"
3)  1) news_id
    2) "19734"
    3) provider
    4) "Marcin"
....

I applied a temporary workaround - single quotes instead of double quotes, which seems to work fine.
Sadly I was not able to identify a place in the code, which might be the reason for this behaviour. I hope you will be able to.

Thank you!
Marcin

Unable to catch RedisClient\Exception\EmptyResponseException

I fully admit that I may be doing this wrong, but the call stack is not being very helpful in terms of where I need to catch this connection error.

Here is what I am doing, please let me know if you see something out of the ordinary.

Using Pimple:

$container['redis'] = function (Container $c) {
    $settings = $c['app.settings']['redis'];

    try {
        $redis = ClientFactory::create([
            'server'  => \sprintf('%s:%s', $settings['host'], $settings['port']),
            'timeout' => $settings['timeout'],
        ]);

        return $redis;
    } catch (EmptyResponseException $e) {
        $c['app.logger']->error('Failed to connect to redis... Retrying...');
       
        // Try to reconnect
        return $container['redis']($c);
    }
};

Full Stack trace from php:

Fatal error: Uncaught RedisClient\Exception\EmptyResponseException: Empty response. Please, check connection timeout. in /var/www/vendor/cheprasov/php-redis-client/src/RedisClient/Protocol/RedisProtocol.php:111
Stack trace:
#0 /var/www/vendor/cheprasov/php-redis-client/src/RedisClient/Protocol/RedisProtocol.php(164): RedisClient\Protocol\RedisProtocol->read()
#1 /var/www/vendor/cheprasov/php-redis-client/src/RedisClient/Protocol/RedisProtocol.php(171): RedisClient\Protocol\RedisProtocol->sendRaw('*5\r\n$7\r\nEVALSHA...')
#2 /var/www/vendor/cheprasov/php-redis-client/src/RedisClient/Client/AbstractRedisClient.php(174): RedisClient\Protocol\RedisProtocol->send(Array)
#3 /var/www/vendor/cheprasov/php-redis-client/src/RedisClient/Client/AbstractRedisClient.php(155): RedisClient\Client\AbstractRedisClient->executeProtocolCommand(Object(RedisClient\Protocol\RedisProtocol), Array, Array)
#4 /var/www/vendor/cheprasov/php-redis-client/src/RedisClient/Client/AbstractRedisClient.php(142): RedisClient\Client\AbstractRedisClient->exec in /var/www/vendor/cheprasov/php-redis-client/src/RedisClient/Protocol/RedisProtocol.php on line 111

Note: It only throws the exception every once in a while. It could be something AWS related, but I'd still like to be able to catch the error and retry the connection.

No reads on replicas in our cluster

Hello,

we are currently running an Elasticcache Redis Cluster with 3 shards and one read replica per shard.
Everything is working fine and the replica takes over if one of the master shards fails.
But: We have no reads on the read replicas. They are just there relicating the masters and waiting for one of them to fail.

This seems like a waste of ressources for me. Is there a way to change that and use the replicas for read requests?

Thank you!

[Feature request] Running commands in async mode

Extending predis pipeline, I was able to simultanously send the same command to multiple Redis instances and wait for the responses and process them in PHP to form the final result (map-reduce). As far as I know, this wasn't possible with phpredis. I am wondering if you can officially add such a feature to php-redis-client as well.
Here is my predis code and an example test:

class TwoStepPipeline extends Pipeline
{
    /**
     * @var ConnectionInterface
     */
    protected $waitingConnection;

    /**
     * @var \SplQueue
     */
    protected $waitingCommands;

    /**
     * Flush the queue without waiting for the responses.
     *
     * @inheritdoc
     */
    protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands)
    {
        if (! $this->waitingCommands) {
            $this->waitingCommands = new \SplQueue();
        }

        foreach ($commands as $command) {
            $connection->writeRequest($command);
            $this->waitingCommands->enqueue($command);
        }

        $this->waitingConnection = $connection;

        return [];
    }

    /**
     * Read the responses of the last flushed queue.
     *
     * @return array
     */
    public function wait()
    {
        $responses = array();
        $exceptions = $this->throwServerExceptions();

        while (!$this->waitingCommands->isEmpty()) {
            $command = $this->waitingCommands->dequeue();
            $response = $this->waitingConnection->readResponse($command);

            if (!$response instanceof ResponseInterface) {
                $responses[] = $command->parseResponse($response);
            } elseif ($response instanceof ErrorResponseInterface && $exceptions) {
                $this->exception($this->waitingConnection, $response);
            } else {
                $responses[] = $response;
            }
        }

        $this->waitingConnection = null;

        return $responses;
    }
}

    public function test_two_step_pipeline()
    {
        $shards = ['redis-0', 'redis-1', 'redis-2', 'redis-3', 'redis-4', 'redis-5', 'redis-6', 'redis-7'];
        $pipelines = [];
        //Write to all pipes
        foreach ($shards as $shard) {
            $pipeline = new TwoStepPipeline(Redis::connection($shard)->client());
            $pipeline->execute(function ($redis) {
                $redis->set('key1', 'value1');
                $redis->set('key2', 'value2');
            });
            $pipeline->execute(function ($redis) {
                $redis->set('key3', 'value3');
                $redis->set('key4', 'value4');
            });
            $pipelines[] = $pipeline;
        }
        // Wait for all the responses
        foreach ($pipelines as $pipeline) {
            $pipeline->wait();
        }
        foreach ($shards as $shard) {
            $this->assertEquals('value1', Redis::connection($shard)->get('key1'));
            $this->assertEquals('value2', Redis::connection($shard)->get('key2'));
            $this->assertEquals('value3', Redis::connection($shard)->get('key3'));
            $this->assertEquals('value4', Redis::connection($shard)->get('key4'));
        }
    }

Our story: In our production use-case, the commands to be run in parallel need some heavy cpu-bound operations. For us, this was the only way to utilize the resources in order to reduce the time needed to process each request. Now that predis is not maintained, we are kind of in trouble if we can't migrate to a new solution.

Thanks in advance for your help and guides.

8.1.1: Deprecated: Optional parameter $after declared before required parameter $value

Deprecated: Optional parameter $after declared before required parameter $value is implicitly treated as a required parameter in C:\wamp\www\vendor\cheprasov\php-redis-client\src\RedisClient\Command\Traits\Version2x6\ListsCommandsTrait.php on line 94
public function linsert($key, $after = true, $pivot, $value) {
remove "= true"

PHP 8.1.1
php-redis-client: latest version

Variadic functions when array in arg

This project seems pretty well developed and I guess it'll rock on PHP 8 where people will more and more write and seek for extensions written directly in php.

Here is my suggestion:
Functions like hmget can easily become variadic to comply with Redis docs and without breaking changes for users like that:

    public function hmget($key, ...$fields) {
        if(is_array($fields[0]))
            $fields = $fields[0];
        return $this->returnCommand(['HMGET'], $key, [$key, (array) $fields]);
    }

This way, it can be called:

    $redis->hmget("key", "field1", "field2");
        or
    $redis->hmget("key", ["field1", "field2"]);

Calling time() from pipeline or multi results in an error

Hello! Thanks for this client, works great.

One thing, it seems because there's a $parserId set when the ->time() command is called in multi cases (not sure about pipeline too maybe?), that the client tries to parse the QUEUED string result right away resulting in errors like this:

FRAME    3. RedisClient\\Client\\AbstractRedisClient->executePipeline(RedisClient\\Pipeline\\Version\\Pipeline5x0 object) /home/vagrant/ttus/debesys/risk/vendor/cheprasov/php-redis-client/src/RedisClient/Client/AbstractRedisClient.php:281
FRAME    4. RedisClient\\Pipeline\\AbstractPipeline->parseResponse(Array) /home/vagrant/ttus/debesys/risk/vendor/cheprasov/php-redis-client/src/RedisClient/Client/AbstractRedisClient.php:235
FRAME    5. RedisClient\\Command\\Response\\ResponseParser::parse(3, "QUEUED") /home/vagrant/ttus/debesys/risk/vendor/cheprasov/php-redis-client/src/RedisClient/Pipeline/AbstractPipeline.php:110
 FRAME    6. RedisClient\\Command\\Response\\ResponseParser::parseTime("QUEUED") /home/vagrant/ttus/debesys/risk/vendor/cheprasov/php-redis-client/src/RedisClient/Command/Response/ResponseParser.php:34
FRAME    7. myErrorHandler(4096, "Argument 1 passed to RedisClient\\Command\\Response\\ResponseParser::parseTime() must be of the type array, string given, called in....

Thanks again for the great client!

Unittests failed

Hello,

When running the testsuite, one test fails:

................................F

Time: 338 ms, Memory: 13.25Mb

There was 1 failure:

1) Test\Integration\Version2x6\KeysCommandsTest::test_pttl
Failed asserting that -2 is identical to -1.

~/Sites/php-redis-client/tests/Integration/Version2x6/KeysCommandsTest.php:292

EmptyResponseException when executing flushdb() right after brpop().

So I'm writing a small library to wrap redis data types into PHP objects. Simple enough.
But when testing the following:

public function testRpopTimeout(): void
{
    $list = new RedisList(self::$client, 'rpopTimeout');
    $this->expectException(EmptyResponseException::class);
    $list->rpop(1);
}

and then immediately cleaning up the database by using

public static function tearDownAfterClass()
{
    self::$client->flushdb();
}

I get the following error and stack trace:

PHP Fatal error:  Uncaught RedisClient\Exception\EmptyResponseException: Empty response. Please, check connection timeout. in /mnt/e/Projects/phpstorm/RedisObjects/vendor/cheprasov/php-redis-client/src/RedisClient/Protocol/RedisProtocol.php:111
Stack trace:
#0 /mnt/e/Projects/phpstorm/RedisObjects/vendor/cheprasov/php-redis-client/src/RedisClient/Protocol/RedisProtocol.php(164): RedisClient\Protocol\RedisProtocol->read()
#1 /mnt/e/Projects/phpstorm/RedisObjects/vendor/cheprasov/php-redis-client/src/RedisClient/Protocol/RedisProtocol.php(171): RedisClient\Protocol\RedisProtocol->sendRaw('*1\r\n$7\r\nFLUSHDB...')
#2 /mnt/e/Projects/phpstorm/RedisObjects/vendor/cheprasov/php-redis-client/src/RedisClient/Client/AbstractRedisClient.php(174): RedisClient\Protocol\RedisProtocol->send(Array)
#3 /mnt/e/Projects/phpstorm/RedisObjects/vendor/cheprasov/php-redis-client/src/RedisClient/Client/AbstractRedisClient.php(155): RedisClient\Client\AbstractRedisClient->executeProtocolCommand(Object(RedisClient\Protocol\RedisProtocol), Array, NULL) in /mnt/e/Projects/phpstorm/RedisObjects/vendor/cheprasov/php-redis-client/src/RedisClient/Protocol/RedisProtocol.php on line 111

I can't figure out whats going on.

Oh and yeah the command doesn't even reach Redis (this is the last one before the exception):

1518690040.542776 [8 127.0.0.1:52511] "BRPOP" "rpopTimeout" "1"

Any idea what might be the problem here?

Using Redis 4.0.1 but not RedisClient4x0, because I want this to be compatible with as many versions as possible.

Thanks.
K. Becker

EDIT: I played around a bit and noticed this behaviour only when using blocking methods.
For example, if I use brpop and after that test some other methods like

$list->rpush(1, 2, 3);
self::assertSame(1, $list->lpop());

lpop() does NOT pop 1 but instead null. Still figuring out why...

Cannot connect to database

Hello, I'm running into some issues getting php-redis-client working in my laravel project. I included it in my composer packages and installed it, however, the client cannot connect to the redis.

"stream_socket_client(): unable to connect to tcp://127.0.0.1:6379 (Connection refused)"

screen shot 2018-06-21 at 1 04 14 pm

As you can see the redis server is running and I can connect to it using the exact same host and port the codebase is attempting to use:

screen shot 2018-06-21 at 1 04 25 pm

screen shot 2018-06-21 at 1 04 44 pm

Here's my code for reference.

screen shot 2018-06-21 at 1 06 02 pm

Timeout issue

Hi,
I am trying to understand what the timeout is for.

$Redis = ClientFactory::create([
    'server' => 'redis:6379', // external redis server 
    'timeout' => 2,
]);

In case the Redis server is unavailable Redis instance doesn't really throw an error or stops after 2 seconds (with the error of timeout).
How can I limite the timeout if the redis server is unavailable ?

An error occurred while saving a sufficiently long string

Hello!

Redis server: 3.2.100 ( OpenServer, Windows )

PHP 7.1 x64

String length === 14000 ( Serialized multilevel array )

Timeout = 60 (!!!)

Method = $Redis->set()

Result:
Uncaught RedisClient\Exception\EmptyResponseException: Empty response. Please, check connection timeout.

Why??? What am I doing wrong?

Any pitfalls to STREAM_CLIENT_PERSISTENT ?

I'm just wondering if you're aware of any downsides to enabling STREAM_CLIENT_PERSISTENT for this lib, for a php-fpm or mod_php app? I don't see it recommended in the README, but I see it's mentioned as supported.

I'm evaluating which Redis client implementation I want to use, whether I'll need to use a PHP extension for performance or if this implementation would be fast enough most of the time.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.