Async Redis client implementation, built on top of ReactPHP.

Overview

clue/reactphp-redis

CI status installs on Packagist

Async Redis client implementation, built on top of ReactPHP.

Redis is an open source, advanced, in-memory key-value database. It offers a set of simple, atomic operations in order to work with its primitive data types. Its lightweight design and fast operation makes it an ideal candidate for modern application stacks. This library provides you a simple API to work with your Redis database from within PHP. It enables you to set and query its data or use its PubSub topics to react to incoming events.

  • Async execution of Commands - Send any number of commands to Redis in parallel (automatic pipeline) and process their responses as soon as results come in. The Promise-based design provides a sane interface to working with async responses.
  • Event-driven core - Register your event handler callbacks to react to incoming events, such as an incoming PubSub message event.
  • Lightweight, SOLID design - Provides a thin abstraction that is just good enough and does not get in your way. Future or custom commands and events require no changes to be supported.
  • Good test coverage - Comes with an automated tests suite and is regularly tested against versions as old as Redis v2.6 and newer.

Table of Contents

Support us

We invest a lot of time developing, maintaining and updating our awesome open-source projects. You can help us sustain this high-quality of our work by becoming a sponsor on GitHub. Sponsors get numerous benefits in return, see our sponsoring page for details.

Let's take these projects to the next level together! 🚀

Quickstart example

Once installed, you can use the following code to connect to your local Redis server and send some requests:



require __DIR__ . '/vendor/autoload.php';

$factory = new Clue\React\Redis\Factory();
$redis = $factory->createLazyClient('localhost:6379');

$redis->set('greeting', 'Hello world');
$redis->append('greeting', '!');

$redis->get('greeting')->then(function ($greeting) {
    // Hello world!
    echo $greeting . PHP_EOL;
});

$redis->incr('invocation')->then(function ($n) {
    echo 'This is invocation #' . $n . PHP_EOL;
});

// end connection once all pending requests have been resolved
$redis->end();

See also the examples.

Usage

Commands

Most importantly, this project provides a Client instance that can be used to invoke all Redis commands (such as GET, SET, etc.).

$redis->get($key);
$redis->set($key, $value);
$redis->exists($key);
$redis->expire($key, $seconds);
$redis->mget($key1, $key2, $key3);

$redis->multi();
$redis->exec();

$redis->publish($channel, $payload);
$redis->subscribe($channel);

$redis->ping();
$redis->select($database);

// many more…

Each method call matches the respective Redis command. For example, the $redis->get() method will invoke the GET command.

All Redis commands are automatically available as public methods via the magic __call() method. Listing all available commands is out of scope here, please refer to the Redis command reference.

Any arguments passed to the method call will be forwarded as command arguments. For example, the $redis->set('name', 'Alice') call will perform the equivalent of a SET name Alice command. It's safe to pass integer arguments where applicable (for example $redis->expire($key, 60)), but internally Redis requires all arguments to always be coerced to string values.

Each of these commands supports async operation and returns a Promise that eventually fulfills with its results on success or rejects with an Exception on error. See also the following section about promises for more details.

Promises

Sending commands is async (non-blocking), so you can actually send multiple commands in parallel. Redis will respond to each command request with a response message, pending commands will be pipelined automatically.

Sending commands uses a Promise-based interface that makes it easy to react to when a command is completed (i.e. either successfully fulfilled or rejected with an error):

$redis->get($key)->then(function (string $value) {
    var_dump($value);
}, function (Exception $e) {
    echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

PubSub

This library is commonly used to efficiently transport messages using Redis' Pub/Sub (Publish/Subscribe) channels. For instance, this can be used to distribute single messages to a larger number of subscribers (think horizontal scaling for chat-like applications) or as an efficient message transport in distributed systems (microservice architecture).

The PUBLISH command can be used to send a message to all clients currently subscribed to a given channel:

$channel = 'user';
$message = json_encode(array('id' => 10));
$redis->publish($channel, $message);

The SUBSCRIBE command can be used to subscribe to a channel and then receive incoming PubSub message events:

$channel = 'user';
$redis->subscribe($channel);

$redis->on('message', function ($channel, $payload) {
    // pubsub message received on given $channel
    var_dump($channel, json_decode($payload));
});

Likewise, you can use the same client connection to subscribe to multiple channels by simply executing this command multiple times:

$redis->subscribe('user.register');
$redis->subscribe('user.join');
$redis->subscribe('user.leave');

Similarly, the PSUBSCRIBE command can be used to subscribe to all channels matching a given pattern and then receive all incoming PubSub messages with the pmessage event:

$pattern = 'user.*';
$redis->psubscribe($pattern);

$redis->on('pmessage', function ($pattern, $channel, $payload) {
    // pubsub message received matching given $pattern
    var_dump($channel, json_decode($payload));
});

Once you're in a subscribed state, Redis no longer allows executing any other commands on the same client connection. This is commonly worked around by simply creating a second client connection and dedicating one client connection solely for PubSub subscriptions and the other for all other commands.

The UNSUBSCRIBE command and PUNSUBSCRIBE command can be used to unsubscribe from active subscriptions if you're no longer interested in receiving any further events for the given channel and pattern subscriptions respectively:

$redis->subscribe('user');

Loop::addTimer(60.0, function () use ($redis) {
    $redis->unsubscribe('user');
});

Likewise, once you've unsubscribed the last channel and pattern, the client connection is no longer in a subscribed state and you can issue any other command over this client connection again.

Each of the above methods follows normal request-response semantics and return a Promise to await successful subscriptions. Note that while Redis allows a variable number of arguments for each of these commands, this library is currently limited to single arguments for each of these methods in order to match exactly one response to each command request. As an alternative, the methods can simply be invoked multiple times with one argument each.

Additionally, can listen for the following PubSub events to get notifications about subscribed/unsubscribed channels and patterns:

$redis->on('subscribe', function ($channel, $total) {
    // subscribed to given $channel
});
$redis->on('psubscribe', function ($pattern, $total) {
    // subscribed to matching given $pattern
});
$redis->on('unsubscribe', function ($channel, $total) {
    // unsubscribed from given $channel
});
$redis->on('punsubscribe', function ($pattern, $total) {
    // unsubscribed from matching given $pattern
});

When using the createLazyClient() method, the unsubscribe and punsubscribe events will be invoked automatically when the underlying connection is lost. This gives you control over re-subscribing to the channels and patterns as appropriate.

API

Factory

The Factory is responsible for creating your Client instance.

$factory = new Clue\React\Redis\Factory();

This class takes an optional LoopInterface|null $loop parameter that can be used to pass the event loop instance to use for this object. You can use a null value here in order to use the default loop. This value SHOULD NOT be given unless you're sure you want to explicitly use a given event loop instance.

If you need custom connector settings (DNS resolution, TLS parameters, timeouts, proxy servers etc.), you can explicitly pass a custom instance of the ConnectorInterface:

$connector = new React\Socket\Connector(array(
    'dns' => '127.0.0.1',
    'tcp' => array(
        'bindto' => '192.168.10.1:0'
    ),
    'tls' => array(
        'verify_peer' => false,
        'verify_peer_name' => false
    )
));

$factory = new Clue\React\Redis\Factory(null, $connector);

createClient()

The createClient(string $uri): PromiseInterface method can be used to create a new Client.

It helps with establishing a plain TCP/IP or secure TLS connection to Redis and optionally authenticating (AUTH) and selecting the right database (SELECT).

$factory->createClient('localhost:6379')->then(
    function (Client $redis) {
        // client connected (and authenticated)
    },
    function (Exception $e) {
        // an error occurred while trying to connect (or authenticate) client
    }
);

The method returns a Promise that will resolve with a Client instance on success or will reject with an Exception if the URL is invalid or the connection or authentication fails.

The returned Promise is implemented in such a way that it can be cancelled when it is still pending. Cancelling a pending promise will reject its value with an Exception and will cancel the underlying TCP/IP connection attempt and/or Redis authentication.

$promise = $factory->createClient($uri);

Loop::addTimer(3.0, function () use ($promise) {
    $promise->cancel();
});

The $redisUri can be given in the standard form [redis[s]://][:auth@]host[:port][/db]. You can omit the URI scheme and port if you're connecting to the default port 6379:

// both are equivalent due to defaults being applied
$factory->createClient('localhost');
$factory->createClient('redis://localhost:6379');

Redis supports password-based authentication (AUTH command). Note that Redis' authentication mechanism does not employ a username, so you can pass the password h@llo URL-encoded (percent-encoded) as part of the URI like this:

// all forms are equivalent
$factory->createClient('redis://:h%40llo@localhost');
$factory->createClient('redis://ignored:h%40llo@localhost');
$factory->createClient('redis://localhost?password=h%40llo');

You can optionally include a path that will be used to select (SELECT command) the right database:

// both forms are equivalent
$factory->createClient('redis://localhost/2');
$factory->createClient('redis://localhost?db=2');

You can use the standard rediss:// URI scheme if you're using a secure TLS proxy in front of Redis:

$factory->createClient('rediss://redis.example.com:6340');

You can use the redis+unix:// URI scheme if your Redis instance is listening on a Unix domain socket (UDS) path:

$factory->createClient('redis+unix:///tmp/redis.sock');

// the URI MAY contain `password` and `db` query parameters as seen above
$factory->createClient('redis+unix:///tmp/redis.sock?password=secret&db=2');

// the URI MAY contain authentication details as userinfo as seen above
// should be used with care, also note that database can not be passed as path
$factory->createClient('redis+unix://:secret@/tmp/redis.sock');

This method respects PHP's default_socket_timeout setting (default 60s) as a timeout for establishing the connection and waiting for successful authentication. You can explicitly pass a custom timeout value in seconds (or use a negative number to not apply a timeout) like this:

$factory->createClient('localhost?timeout=0.5');

createLazyClient()

The createLazyClient(string $uri): Client method can be used to create a new Client.

It helps with establishing a plain TCP/IP or secure TLS connection to Redis and optionally authenticating (AUTH) and selecting the right database (SELECT).

$redis = $factory->createLazyClient('localhost:6379');

$redis->incr('hello');
$redis->end();

This method immediately returns a "virtual" connection implementing the Client that can be used to interface with your Redis database. Internally, it lazily creates the underlying database connection only on demand once the first request is invoked on this instance and will queue all outstanding requests until the underlying connection is ready. Additionally, it will only keep this underlying connection in an "idle" state for 60s by default and will automatically close the underlying connection when it is no longer needed.

From a consumer side this means that you can start sending commands to the database right away while the underlying connection may still be outstanding. Because creating this underlying connection may take some time, it will enqueue all oustanding commands and will ensure that all commands will be executed in correct order once the connection is ready. In other words, this "virtual" connection behaves just like a "real" connection as described in the Client interface and frees you from having to deal with its async resolution.

If the underlying database connection fails, it will reject all outstanding commands and will return to the initial "idle" state. This means that you can keep sending additional commands at a later time which will again try to open a new underlying connection. Note that this may require special care if you're using transactions (MULTI/EXEC) that are kept open for longer than the idle period.

While using PubSub channels (see SUBSCRIBE and PSUBSCRIBE commands), this client will never reach an "idle" state and will keep pending forever (or until the underlying database connection is lost). Additionally, if the underlying database connection drops, it will automatically send the appropriate unsubscribe and punsubscribe events for all currently active channel and pattern subscriptions. This allows you to react to these events and restore your subscriptions by creating a new underlying connection repeating the above commands again.

Note that creating the underlying connection will be deferred until the first request is invoked. Accordingly, any eventual connection issues will be detected once this instance is first used. You can use the end() method to ensure that the "virtual" connection will be soft-closed and no further commands can be enqueued. Similarly, calling end() on this instance when not currently connected will succeed immediately and will not have to wait for an actual underlying connection.

Depending on your particular use case, you may prefer this method or the underlying createClient() which resolves with a promise. For many simple use cases it may be easier to create a lazy connection.

The $redisUri can be given in the standard form [redis[s]://][:auth@]host[:port][/db]. You can omit the URI scheme and port if you're connecting to the default port 6379:

// both are equivalent due to defaults being applied
$factory->createLazyClient('localhost');
$factory->createLazyClient('redis://localhost:6379');

Redis supports password-based authentication (AUTH command). Note that Redis' authentication mechanism does not employ a username, so you can pass the password h@llo URL-encoded (percent-encoded) as part of the URI like this:

// all forms are equivalent
$factory->createLazyClient('redis://:h%40llo@localhost');
$factory->createLazyClient('redis://ignored:h%40llo@localhost');
$factory->createLazyClient('redis://localhost?password=h%40llo');

You can optionally include a path that will be used to select (SELECT command) the right database:

// both forms are equivalent
$factory->createLazyClient('redis://localhost/2');
$factory->createLazyClient('redis://localhost?db=2');

You can use the standard rediss:// URI scheme if you're using a secure TLS proxy in front of Redis:

$factory->createLazyClient('rediss://redis.example.com:6340');

You can use the redis+unix:// URI scheme if your Redis instance is listening on a Unix domain socket (UDS) path:

$factory->createLazyClient('redis+unix:///tmp/redis.sock');

// the URI MAY contain `password` and `db` query parameters as seen above
$factory->createLazyClient('redis+unix:///tmp/redis.sock?password=secret&db=2');

// the URI MAY contain authentication details as userinfo as seen above
// should be used with care, also note that database can not be passed as path
$factory->createLazyClient('redis+unix://:secret@/tmp/redis.sock');

This method respects PHP's default_socket_timeout setting (default 60s) as a timeout for establishing the underlying connection and waiting for successful authentication. You can explicitly pass a custom timeout value in seconds (or use a negative number to not apply a timeout) like this:

$factory->createLazyClient('localhost?timeout=0.5');

By default, this method will keep "idle" connections open for 60s and will then end the underlying connection. The next request after an "idle" connection ended will automatically create a new underlying connection. This ensure you always get a "fresh" connection and as such should not be confused with a "keepalive" or "heartbeat" mechanism, as this will not actively try to probe the connection. You can explicitly pass a custom idle timeout value in seconds (or use a negative number to not apply a timeout) like this:

$factory->createLazyClient('localhost?idle=0.1');

Client

The Client is responsible for exchanging messages with Redis and keeps track of pending commands.

Besides defining a few methods, this interface also implements the EventEmitterInterface which allows you to react to certain events as documented below.

__call()

The __call(string $name, string[] $args): PromiseInterface method can be used to invoke the given command.

This is a magic method that will be invoked when calling any Redis command on this instance. Each method call matches the respective Redis command. For example, the $redis->get() method will invoke the GET command.

$redis->get($key)->then(function (string $value) {
    var_dump($value);
}, function (Exception $e) {
    echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

All Redis commands are automatically available as public methods via this magic __call() method. Listing all available commands is out of scope here, please refer to the Redis command reference.

Any arguments passed to the method call will be forwarded as command arguments. For example, the $redis->set('name', 'Alice') call will perform the equivalent of a SET name Alice command. It's safe to pass integer arguments where applicable (for example $redis->expire($key, 60)), but internally Redis requires all arguments to always be coerced to string values.

Each of these commands supports async operation and returns a Promise that eventually fulfills with its results on success or rejects with an Exception on error. See also promises for more details.

end()

The end():void method can be used to soft-close the Redis connection once all pending commands are completed.

close()

The close():void method can be used to force-close the Redis connection and reject all pending commands.

error event

The error event will be emitted once a fatal error occurs, such as when the client connection is lost or is invalid. The event receives a single Exception argument for the error instance.

$redis->on('error', function (Exception $e) {
    echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

This event will only be triggered for fatal errors and will be followed by closing the client connection. It is not to be confused with "soft" errors caused by invalid commands.

close event

The close event will be emitted once the client connection closes (terminates).

$redis->on('close', function () {
    echo 'Connection closed' . PHP_EOL;
});

See also the close() method.

Install

The recommended way to install this library is through Composer. New to Composer?

This project follows SemVer. This will install the latest supported version:

$ composer require clue/redis-react:^2.5

See also the CHANGELOG for details about version upgrades.

This project aims to run on any platform and thus does not require any PHP extensions and supports running on legacy PHP 5.3 through current PHP 8+ and HHVM. It's highly recommended to use the latest supported PHP version for this project.

Tests

To run the test suite, you first need to clone this repo and then install all dependencies through Composer:

$ composer install

To run the test suite, go to the project root and run:

$ vendor/bin/phpunit

The test suite contains both unit tests and functional integration tests. The functional tests require access to a running Redis server instance and will be skipped by default.

If you don't have access to a running Redis server, you can also use a temporary Redis Docker image:

$ docker run --net=host redis

To now run the functional tests, you need to supply your login details in an environment variable like this:

$ REDIS_URI=localhost:6379 vendor/bin/phpunit

License

This project is released under the permissive MIT license.

Did you know that I offer custom development services and issuing invoices for sponsorships of releases and for contributions? Contact me (@clue) for details.

Comments
  • LazyClient: no error messages

    LazyClient: no error messages

    Suppose I have a redis process running on a standard port and in my code I make a mistake by incorrectly specifying the port. How do I know about the error?

    <?php
    
    $loader = require __DIR__ . '/vendor/autoload.php';
    
    use \Clue\React\Redis\Factory;
    use \Clue\React\Redis\Client;
    
    $loop = React\EventLoop\Factory::create();
    
    $factory = new Factory($loop);
    
    $client = $factory->createLazyClient('redis://127.0.0.1:9006');
    
    $client->on('error', function (Exception $e) {
        echo "Error: " . $e . PHP_EOL;
    });
    
    $client->on('close', function () {
        echo "Connection closed" . PHP_EOL;
    });
    
    $client->set('qwe', 1234)->then(function ($r) use ($loop) {
        $loop->stop();
    });
    
    $loop->run();
    
    
    question 
    opened by Logioniz 11
  • Can't connect to local DNS name

    Can't connect to local DNS name

    I have a local DNS alias in my /etc/hosts just named redis wich points to my Redis server when I try to create the client with the Factory::createClient using this DNS the React\Dns\Resolver\Resolver search for it in this server 8.8.8.8 (Google's DNS Server) and can't resolve because it's local.

    question help wanted 
    opened by rodrigowbazevedo 11
  • Fix redis:// being rejected and username:password passing invalid auth to Redis

    Fix redis:// being rejected and username:password passing invalid auth to Redis

    As discussed in #40, Sending auth username:password to a Redis server doesn't make any sense right now. Some services such as Redis Cloud automatically setup the urls in the form of bogus_username:password@server and only the password part should be sent on to Redis. Also redis:// is a valid schema prefix.

    new feature BC break 
    opened by daneren2005 7
  • Failure to connect is not indicated with rejected promise

    Failure to connect is not indicated with rejected promise

    Using essentially this code:

    $_factory = new Factory($loop);
    $_factory
        ->createClient("redis://$_host:$_port")
        ->then(function(Client $client) use($_host, $_port) {
            $logger->info("Redis connected to $_host:$_port");
        },
        function(\Exception $ex) {
            $logger->error("Failed to connect to Redis:\n$e");
        });
    

    I have Redis turned off, so connections should fail. However, the code is always printing "Redis connected to host:port" instead of "Failed to connect to Redis:...".

    The only way I can seem to detect that it failed to connect is to subscribe to the "close" event, which does fire when the connection attempt fails. However, this cannot be so easily included in a promise chain. In fact, since there is no opposite event (we only have close for failure, but not open for success), it's actually impossible to detect if the connection succeeded. The only way I can think of is to attempt a command like PING and chain promises from there. But surely there must be a better way. Am I missing something? Is this something that can be fixed?

    question help wanted 
    opened by JoelFeiner 6
  • Switch to use React / Socket, instead of Socket Client

    Switch to use React / Socket, instead of Socket Client

    Hi Clue

    Due to the major changes in react (which you have contributed to), I am sadly no longer able to use the package. The main reason is that this package depends on the React / SocketClient package.

    Can you please update the package's dependencies and release it as version 2.x (important so that you don't break compatibility for those that use version 1.x)

    new feature maintenance BC break 
    opened by aedart 6
  • Don't work with php7 & event

    Don't work with php7 & event

    #php version:7.0.18 event extension : 2.3.0

    require_once __DIR__ . '/../../vendor/autoload.php';
    
    $loop = React\EventLoop\Factory::create();
    $factory = new Factory($loop);
    $factory->createClient("127.0.0.1:6379")->then(function (Client $client) use ($loop) {
        echo '# connected! Entering interactive mode, hit CTRL-D to quit' . PHP_EOL;
    
        $client->on('data', function (ModelInterface $data) {
            if ($data instanceof ErrorReply) {
                echo '# error reply: ' . $data->getMessage() . PHP_EOL;
            } else {
                echo '# reply: ' . json_encode($data->getValueNative()) . PHP_EOL;
            }
        });
    
        $loop->addReadStream(STDIN, function () use ($client, $loop) {
            $line = fgets(STDIN);
            if ($line === false || $line === '') {
                echo '# CTRL-D -> Ending connection...' . PHP_EOL;
                $client->end();
            } else {
                $line = rtrim($line);
    
                if ($line === '') {
    
                } else {
                    $params = explode(' ', $line);
                    $method = array_shift($params);
                    call_user_func_array(array($client, $method), $params);
                }
            }
        });
    
        $client->on('close', function() use ($loop) {
            echo '## DISCONNECTED' . PHP_EOL;
    
            $loop->removeReadStream(STDIN);
        });
    }, function (Exception $error) {
        echo 'CONNECTION ERROR: ' . $error->getMessage() . PHP_EOL;
        exit(1);
    });
    
    

    Only show:

    ➜  Test git:(master) ✗ php test2.php
    # connecting to redis...
    
    opened by sukui 6
  • Incorrect PHPDoc for pub/sub

    Incorrect PHPDoc for pub/sub

    https://github.com/clue/reactphp-redis/blob/a3630f14cc0852d9b0d4bf8f5732479389215792/src/Client.php#L14

    This should be publish not message. per: https://redis.io/topics/pubsub.

    Code works fine, but PHPStan is telling me that the publish method doesn't exist. I can workaround with an error override, but it'd be nice to have it fixed.

    question 
    opened by JoelFeiner 5
  • Extending StreamingClient without rewriting Factory

    Extending StreamingClient without rewriting Factory

    Hello!

    How would one extend StreamingClient and use extended class without rewriting the whole Factory class?

    It seems to be easy to implement the Factory in such way that it would create a custom (extended) StreamingClient by just providing the class to use (https://github.com/clue/reactphp-redis/blob/master/src/Factory.php#L69) somewhere in the Factory.

    Maybe something like:

    $factory->setClientClass(...);
    

    Which would override the default Clue\React\Redis\StreamingClient.

    That way we can easily use Factory and build upon (extend) StreamingClient. It just seems such a waste to rewrite (and maintain) Factory class just to change the returned StreamingClient object.

    I can make PR for this if the idea is feasible.

    Thanks for great software!

    Cheers, Dejan

    question new feature 
    opened by dmarkic 5
  • Add redis sentinel on lazy client

    Add redis sentinel on lazy client

    Hello !

    This is my current implementation of Redis Sentinel on the lazy client. I know it can be useful for some people. I'm lacking of time to finish the PR actually, but I would like to get some comments about it.

    I will be happy to get some advices. Have a nice day.

    • [ ] Add Sentinel on Lazy Client
    • [ ] Add Sentinel on Standard Client
    • [ ] Update documentation
    • [ ] Add tests
    new feature help wanted 
    opened by Th3Mouk 5
  • How to stop the loop without closing the connections

    How to stop the loop without closing the connections

    Is there any way to stop the react event loop and keeping the redis connection open? I just want to use this package to send a command to multiple instances of redis at once and wait for all the responses inside a laravel application. I don't think it is a good idea to add extra overhead to reconnect everytime I need to do that.

    question 
    opened by halaei 5
  • use `redis-react` to subscribe message,how to reconnect

    use `redis-react` to subscribe message,how to reconnect

    my code is below

    $factory->createClient("{$redisHost}:{$redisPort}")->then(function (Client $client) {
        $client->subscribe('device_change')->then(function () {
            echo 'Now subscribed to channel ' . PHP_EOL;
        });
    
        $client->on('message', function ($channel, $message) {
            echoln("get message"));
        });
    
        $client->on('close', function () {
            echoln('close');
    
            //set a Timer to reconnect the connect
            $timer_id = Timer::add(5, function () use (&$timer_id) {
                // todo ,here how to reconnect
                // 
    
            });
    
        });
        $client->on('error', function (Exception $e) {
            echoln('error' . $e->getMessage());
        });
    });
    
    question 
    opened by BelieveCode 5
  • Use reactphp/async instead of clue/reactphp-block

    Use reactphp/async instead of clue/reactphp-block

    See https://github.com/reactphp/async

    Ref: https://github.com/clue/reactphp-block/pull/67, https://github.com/clue/reactphp-block/issues/68, https://github.com/reactphp/http/pull/464 and https://github.com/clue/reactphp-mq/pull/34

    opened by dinooo13 2
  • Potential risk that blocking calls never return

    Potential risk that blocking calls never return

    I've noticed that when executing a BLPOP and the Redis-Server "goes away" without closing the socket (very rare occasions, especially on production) the promise never returns making the whole connection unusable. If the server closes the connection, all is fine.

    I know the library has by design no special handling for any Redis command but maybe in this case internally adding React\Promise\Timer\timeout would make sense, at least that's how I handle these cases now...

    opened by pfk84 1
  • Feature sentinel

    Feature sentinel

    Redis sentinel master auto discovery This is alpha version, it may contains major bugs. Use it only on own risk! Introduced new class SentinelClient with public methods: masterAddress() - for simple master address discovery from sentinel masterConnection() - for complete check for master connection according to https://redis.io/docs/reference/sentinel-clients/

    Some tests included. I've changed ci.yml to support sentinel tests Related to https://github.com/clue/reactphp-redis/issues/69

    opened by sartor 2
  • Simplify API, instantiate `RedisClient` instead of `Factory`

    Simplify API, instantiate `RedisClient` instead of `Factory`

    There are plans to simplify the API by removing the Factory and instead replacing it with a simpler RedisClient constructor. In other words, this:

    $factory = new Clue\React\Redis\Factory();
    $redis = $factory->createLazyClient('localhost');
    
    $redis->incr('counter');
    

    Could potentially look like this:

    $redis = new Clue\React\Redis\RedisClient('localhost');
    
    $redis->incr('counter');
    

    Internally, the RedisClient would work similar to what is currently called a "lazy" client, but we'll probably get rid of this wording altogether. This means it would automatically create the underlying connection to Redis only when it's actually needed and will close this connection after a short while when it is not in use anymore. What is currently called "idle" time would probably be reworded to be more of a "keepalive" with much lower defaults.

    Additionally, we'll probably fire "open" and "close" events as needed (loosely inspired by https://github.com/clue/reactphp-eventsource). Unlike the current version, my idea is to also keep the instance in a usable state even after calling close() on it. Reusing the same instance at a later time makes a lot more sense in long-running applications instead of locking it in an unusable state.

    This is obviously a BC break, but there are no plans to break any of the existing code besides this. Most consumers should be able to upgrade to the new instantiation without issues, actual query APIs etc. would not be affected.

    The same API discussion also applies to SQLite (https://github.com/clue/reactphp-sqlite/issues/47), MySQL (https://github.com/friends-of-reactphp/mysql/issues/147) and others Builds on top of #114

    new feature help wanted BC break 
    opened by clue 0
Releases(v2.6.0)
  • v2.6.0(May 9, 2022)

  • v2.5.0(Aug 31, 2021)

    • Feature: Simplify usage by supporting new default loop and new Socket API. (#114 and #115 by @SimonFrings)

      // old (still supported)
      $factory = new Clue\React\Redis\Factory($loop);
      
      // new (using default loop)
      $factory = new Clue\React\Redis\Factory();
      
    • Feature: Improve error reporting, include Redis URI and socket error codes in all connection errors. (#116 by @clue)

    • Documentation improvements and updated examples. (#117 by @clue, #112 by @Nyholm and #113 by @PaulRotmann)

    • Improve test suite and use GitHub actions for continuous integration (CI). (#111 by @SimonFrings)

    Source code(tar.gz)
    Source code(zip)
  • v2.4.0(Sep 25, 2020)

    • Fix: Fix dangling timer when lazy connection closes with pending commands. (#105 by @clue)

    • Improve test suite and add .gitattributes to exclude dev files from exports. Prepare PHP 8 support, update to PHPUnit 9 and simplify test matrix. (#96 and #97 by @clue and #99, #101 and #104 by @SimonFrings)

    Source code(tar.gz)
    Source code(zip)
  • v2.3.0(Mar 11, 2019)

    • Feature: Add new createLazyClient() method to connect only on demand and implement "idle" timeout to close underlying connection when unused. (#87 and #88 by @clue and #82 by @WyriHaximus)

      $client = $factory->createLazyClient('redis://localhost:6379');
      
      $client->incr('hello');
      $client->end();
      
    • Feature: Support cancellation of pending connection attempts. (#85 by @clue)

      $promise = $factory->createClient($redisUri);
      
      $loop->addTimer(3.0, function () use ($promise) {
          $promise->cancel();
      });
      
    • Feature: Support connection timeouts. (#86 by @clue)

      $factory->createClient('localhost?timeout=0.5');
      
    • Feature: Improve Exception messages for connection issues. (#89 by @clue)

      $factory->createClient('redis://localhost:6379')->then(
          function (Client $client) {
              // client connected (and authenticated)
          },
          function (Exception $e) {
              // an error occurred while trying to connect (or authenticate) client
              echo $e->getMessage() . PHP_EOL;
              if ($e->getPrevious()) {
                  echo $e->getPrevious()->getMessage() . PHP_EOL;
              }
          }
      );
      
    • Improve test suite structure and add forward compatibility with PHPUnit 7 and PHPUnit 6 and test against PHP 7.1, 7.2, and 7.3 on TravisCI. (#83 by @WyriHaximus and #84 by @clue)

    • Improve documentation and update project homepage. (#81 and #90 by @clue)

    Source code(tar.gz)
    Source code(zip)
  • v2.2.0(Jan 24, 2018)

    • Feature: Support communication over Unix domain sockets (UDS) (#70 by @clue)

      // new: now supports redis over Unix domain sockets (UDS)
      $factory->createClient('redis+unix:///tmp/redis.sock');
      
    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Sep 25, 2017)

    • Feature: Update Socket dependency to support hosts file on all platforms (#66 by @clue)

      This means that connecting to hosts such as localhost (and for example those used for Docker containers) will now work as expected across all platforms with no changes required:

      $factory->createClient('localhost');
      
    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(Sep 20, 2017)

    A major compatibility release to update this package to support all latest ReactPHP components!

    This update involves a minor BC break due to dropped support for legacy versions. We've tried hard to avoid BC breaks where possible and minimize impact otherwise. We expect that most consumers of this package will actually not be affected by any BC breaks, see below for more details.

    • BC break: Remove all deprecated APIs, default to redis:// URI scheme and drop legacy SocketClient in favor of new Socket component. (#61 by @clue)

      All of this affects the Factory only, which is mostly considered "advanced usage". If you're affected by this BC break, then it's recommended to first update to the intermediary v1.2.0 release, which allows you to use the redis:// URI scheme and a standard ConnectorInterface and then update to this version without causing a BC break.

    • BC break: Remove uneeded data event and support for advanced MONITOR command for performance and consistency reasons and remove underdocumented isBusy() method. (#62, #63 and #64 by @clue)

    • Feature: Forward compatibility with upcoming Socket v1.0 and v0.8 and EventLoop v1.0 and Evenement v3 (#65 by @clue)

    Source code(tar.gz)
    Source code(zip)
  • v1.2.0(Sep 19, 2017)

    • Feature: Support redis[s]:// URI scheme and deprecate legacy URIs (#60 by @clue)

      $factory->createClient('redis://:secret@localhost:6379/4');
      $factory->createClient('redis://localhost:6379?password=secret&db=4');
      
    • Feature: Factory accepts Connector from Socket and deprecate legacy SocketClient (#59 by @clue)

      If you need custom connector settings (DNS resolution, TLS parameters, timeouts, proxy servers etc.), you can explicitly pass a custom instance of the ConnectorInterface:

      $connector = new \React\Socket\Connector($loop, array(
          'dns' => '127.0.0.1',
          'tcp' => array(
              'bindto' => '192.168.10.1:0'
          ),
          'tls' => array(
              'verify_peer' => false,
              'verify_peer_name' => false
          )
      ));
      
      $factory = new Factory($loop, $connector);
      
    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Sep 18, 2017)

    • Feature: Update SocketClient dependency to latest version (#58 by @clue)

    • Improve test suite by adding PHPUnit to require-dev, fix HHVM build for now again and ignore future HHVM build errors, lock Travis distro so new defaults will not break the build and skip functional integration tests by default (#52, #53, #56 and #57 by @clue)

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(May 20, 2016)

    • First stable release, now following SemVer

    • BC break: Consistent public API, mark internal APIs as such (#38 by @clue)

      // old
      $client->on('data', function (MessageInterface $message, Client $client) {
          // process an incoming message (raw message object)
      });
      
      // new
      $client->on('data', function (MessageInterface $message) use ($client) {
          // process an incoming message (raw message object)
      });
      

    Contains no other changes, so it's actually fully compatible with the v0.5.2 release.

    Source code(tar.gz)
    Source code(zip)
  • v0.5.2(May 20, 2016)

    • Fix: Do not send empty SELECT statement when no database has been given (#35, #36 by @clue)
    • Improve documentation, update dependencies and add first class support for PHP 7
    Source code(tar.gz)
    Source code(zip)
  • v0.5.1(Jan 12, 2015)

  • v0.5.0(Nov 12, 2014)

    • Feature: Support PubSub commands (P)(UN)SUBSCRIBE and watching for "message", "subscribe" and "unsubscribe" events (#24)
    • Feature: Support MONITOR command and watching for "monitor" events (#23)
    • Improve documentation, update locked dependencies and add first class support for HHVM (#25, #26 and others)
    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Aug 25, 2014)

    • BC break: The Client class has been renamed to StreamingClient. Added new Client interface. (#18 and #19)
    • BC break: Rename message event to data. (#21)
    • BC break: The Factory now accepts a LoopInterface as first argument. (#22)
    • Fix: The close event will be emitted once when invoking the Client::close() method or when the underlying stream closes. (#20)
    • Refactored code, improved testability, extended test suite and better code coverage. (#11, #18 and #20)

    Note: This is an intermediary release to ease upgrading to the imminent v0.5 release.

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(May 31, 2014)

    • First tagged release

    Note: Starts at v0.3 because previous versions were not tagged. Leaving some room in case they're going to be needed in the future.

    Source code(tar.gz)
    Source code(zip)
Owner
Christian Lück
@reactphp maintainer / Freelance Software Engineer, all things web, passionate about coaching others building awesome things… Reach out if you need help
Christian Lück
🔌 A Doctrine DBAL Driver implementation on top of Swoole Coroutine PostgreSQL extension

Swoole Coroutine PostgreSQL Doctrine DBAL Driver A Doctrine\DBAL\Driver implementation on top of Swoole\Coroutine\PostgreSQL. Getting started Install

Leo Cavalcante 19 Nov 25, 2022
a distributed-redis-lock implementation for hyperf2.*

hyperf-redis-lock English | 中文 an easy redis-based distributed-lock implementation for hyperf 2.*。 This extension features distributed-lock includes b

lysice 11 Nov 8, 2022
Spot v2.x DataMapper built on top of Doctrine's Database Abstraction Layer

Spot DataMapper ORM v2.0 Spot v2.x is built on the Doctrine DBAL, and targets PHP 5.4+. The aim of Spot is to be a lightweight DataMapper alternative

Spot ORM 602 Dec 27, 2022
A simple library to access and manipulate database records. Built on top of Dibi and hardwired for PostgreSQL.

grifart/tables A simple library to access and manipulate database records. Built on top of Dibi and hardwired for PostgreSQL. This library is develope

GRIFART 5 Nov 11, 2022
Async MySQL Framework for PocketMine-MP

MyPigSQL Join my discord: https://discord.gg/2QAPHbqrny Async MySQL Framework for PocketMine-MP Known Issues There are some weird behaviors still, do

Antoine 4 Oct 5, 2022
API abstracting communication with SQL providers (eg: MySQL) on top of PDO inspired by Java JDBC

SQL Data Access API Table of contents: About Configuration Execution Installation Unit Tests Examples Reference Guide About This API is a ultra light

Lucian Gabriel Popescu 0 Jan 9, 2022
A Redis based, fully automated and scalable database cache layer for Laravel

Lada Cache A Redis based, fully automated and scalable database cache layer for Laravel Contributors wanted! Have a look at the open issues and send m

Matt 501 Dec 30, 2022
Поддержка очередей Redis (и на RabbitMq, и на Filesystem, и через DBAL) в Битриксе

Модуль для Битрикса, организующий работу с очередями через Redis (и не только) Поддерживаемый транспорт: Redis RabbitMq Filesystem DBAL Установка comp

Fedy 4 Aug 20, 2021
A Redis bundle for Symfony supporting Predis and PhpRedis

RedisBundle About This bundle integrates Predis and PhpRedis into your Symfony 3.4+ application, providing a fast and convenient interface to Redis. U

Henrik Westphal 1k Dec 22, 2022
This is raw connection between redis server and django python app

Django_Redis This repository contains the code for this blogpost. Running the Application Clone the repository git clone https://github.com/xxl4tomxu9

Tom Xu 1 Sep 15, 2022
Baum is an implementation of the Nested Set pattern for Laravel's Eloquent ORM.

Baum Baum is an implementation of the Nested Set pattern for Laravel 5's Eloquent ORM. For Laravel 4.2.x compatibility, check the 1.0.x branch branch

Estanislau Trepat 2.2k Jan 3, 2023
A data mapper implementation for your persistence model in PHP.

Atlas.Orm Atlas is a data mapper implementation for persistence models (not domain models). As such, Atlas uses the term "record" to indicate that its

null 427 Dec 30, 2022
Adjacency List’ed Closure Table database design pattern implementation for the Laravel framework.

ClosureTable This is a database manipulation package for the Laravel 5.4+ framework. You may want to use it when you need to store and operate hierarc

Yan Ivanov 441 Dec 11, 2022
A drop-in Doctrine ORM 2 implementation for Laravel 5+ and Lumen

Laravel Doctrine ORM A drop-in Doctrine ORM 2 implementation for Laravel 5+ $scientist = new Scientist( 'Albert', 'Einstein' ); $scientist->a

Laravel Doctrine 777 Dec 17, 2022
A minimalistic implementation of asynchronous SQL for PHP.

libSQL A minimalistic implementation of asynchronous SQL for PHP. Installation via DEVirion Install the DEVirion plugin and start your server. This wi

null 10 Dec 7, 2022
Eloquent Repository implementation

Eloquent Repository Eloquent Repository using nilportugues/repository as foundation. Installation Use Composer to install the package: $ composer requ

Nil Portugués Calderó 17 Feb 12, 2022
Eloquent MongoDB Repository Implementation

Eloquent MongoDB Repository Eloquent MongoDB Repository using nilportugues/repository as foundation, using jenssegers/mongodb. Installation Use Compos

Nil Portugués Calderó 18 Feb 12, 2022
A working Firebase http client

PHP library access Firebase RESTful API Installation $ composer require jaredchu/jc-firebase-php Usage Generate a private key in JSON format. Check Fi

Jared Chu 14 Jul 10, 2022
a php client for distributed transaction framework dtm. 分布式事务管理器dtm的php客户端

a client for distributed transaction manager dtm dtmcli 是分布式事务管理器dtm的客户端sdk dtm分布式事务管理服务 DTM是一款跨语言的开源分布式事务管理器,优雅的解决了幂等、空补偿、悬挂等分布式事务难题。提供了简单易用、高性能、易水平扩

null 29 Jan 7, 2023