Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Redis Laravel Package

mix/redis

A lightweight Redis integration for Laravel offering a simple facade, connection management, and helpers for caching, queues, and pub/sub. Configure multiple stores, key prefixes, and serializers with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require mix/redis

Look first at

  • README.md — quickstart snippets for each runtime.
  • src/Redis.php — the main facade; the public method list is your real API surface.
  • src/Connection/Connection.php and src/Connection/ConnectionPool.php — where sockets, auth, SELECT, and pooling live.
  • src/Connection/ConnectionConfig.php — every configurable knob lives here.

Minimal usage (FPM / CLI / any blocking context)

use Mix\Redis\Redis;

$redis = new Redis([
    'host'     => '127.0.0.1',
    'port'     => 6379,
    'password' => '',
    'database' => 0,
    'timeout'  => 5,
]);

$redis->set('user:1', 'alice');
echo $redis->get('user:1');

The Redis object owns a single connection and exposes Redis commands as methods. Construction is cheap enough to do once per request in FPM, or once per worker in CLI.

Minimal usage (Swoole / WorkerMan / coroutine)

In coroutine runtimes you don't share a single socket across coroutines — you take a connection from a pool, use it, and release it. Create the pool during worker start, then borrow per request/coroutine:

use Mix\Redis\Connection\ConnectionPool;
use Mix\Redis\Connection\ConnectionConfig;

// inside onWorkerStart / a bootstrap step, once per worker process
$pool = new ConnectionPool(new ConnectionConfig(
    host: '127.0.0.1',
    port: 6379,
    password: '',
    database: 0,
    timeout: 5.0,
), maxOpen: 10, maxIdle: 10);

// inside your handler (a coroutine)
$conn = $pool->get();
try {
    return $conn->get('user:1');
} finally {
    $pool->release($conn);
}

First use case to ship: a cache-aside wrapper around get/set/del/expire in your service layer. That exercises the config, the connection, and the error path, and it's the 90% use case.

Implementation Patterns

Bind the client(s) once per process, not per call. In FPM, register Redis in your container as a singleton so every controller action shares one socket. In Swoole/WorkerMan, create the ConnectionPool in onWorkerStart and stash it in a worker-local container — constructing it in the request handler defeats pooling and leaks sockets.

Wrap the pool in a small repository. Rather than sprinkling $pool->get() / $pool->release() through handler code, isolate it:

final class Cache
{
    public function __construct(private ConnectionPool $pool) {}

    public function remember(string $key, int $ttl, callable $compute): mixed
    {
        $conn = $this->pool->get();
        try {
            $cached = $conn->get($key);
            if ($cached !== false && $cached !== null) {
                return unserialize($cached);
            }
        } finally {
            $this->pool->release($conn);
        }

        $value = $compute();

        $conn = $this->pool->get();
        try {
            $conn->setex($key, $ttl, serialize($value));
        } finally {
            $this->pool->release($conn);
        }

        return $value;
    }
}

This gives one place to change timeouts, retries, and serialization.

Environment detection is the selling point — exploit it. The same mix/redis API runs on FPM (blocking), CLI crons, Swoole (coroutine), and WorkerMan. Keep your Redis calls behind a thin interface so the transport can differ (single connection vs. pooled) while call sites stay identical. A RedisInterface with get/set/del/expire/incr mapped to either Redis or a pooled adapter lets you write code once and swap bootstrapping per runtime.

Pub/sub pattern. For subscribe, use a dedicated long-lived connection (never the pool) and loop in a worker/cron. The subscriber blocks, so it should live in its own process; the publisher can use the normal pooled path.

Pipeline-style batching. When you're doing many writes in a loop (e.g. invalidation, counter flush), borrow one connection and issue all commands on it before releasing. Releasing inside the loop is the most common performance regression in this package's usage.

Framework glue. In a MixPHP / Swoole / WorkerMan app, the idiomatic integration is: config array → ConnectionConfig → ConnectionPool in the container → injected into services. Read the framework's Redis provider if one exists — it's usually a thin factory over these three classes.

Health checks. Do a ping() on the pooled connection at worker start; if it throws, log and let the worker fail fast rather than surfacing the error on the first user request.

Gotchas and Tips

Fork safety. A ConnectionPool created before Swoole\Process fork or WorkerMan onWorkerStart will be duplicated across children — every child ends up writing to the same socket and the responses will interleave. Always construct the pool inside onWorkerStart (or the equivalent per-process bootstrap), never at top-level script scope.

Always release in a finally. If a handled command throws (timeout, wrong auth, SELECT out of range), the connection is not returned to the pool. Do a few thousand requests and you'll exhaust maxOpen and hang. finally { $pool->release($conn); } is non-negotiable.

get() returns false for a miss, not null. false and null are indistinguishable from "stored empty string" in some Redis clients — here the miss is false, but empty-string values come back as ''. Use === false for miss detection, and never store false as a value unless you also guard for it.

No serialization. Values go over the wire as strings. Anything that isn't a string (arrays, objects, ints you care about keeping typed) must be serialize()/json_encode()'d on write and decoded on read — and you should prefix keys or use a namespace to avoid colliding with other tools writing the same key.

Timeout is per-connection, not per-command. Set a small timeout (1–5s) in ConnectionConfig. Without it, a stalled Redis will block a Swoole coroutine or an FPM worker indefinitely; with coroutine schedulers a blocked worker can cascade into full request starvation.

database is applied per connection. In a pool, every borrowed connection defaults to the configured DB. If you ever need a different DB index for a specific job, use a separate pool with its own ConnectionConfig rather than SELECT-ing on a pooled connection — otherwise you'll return a connection to the pool that subsequent borrowers didn't expect.

Reconnect / failover. The package gives you a single host; there is no built-in sentinel or cluster routing. For failover, wrap the pool constructor in a retry and keep host lists in your own config. Treat connection-level exceptions as fatal to that borrowed connection — don't try to reuse it after a socket error; release it (or let the pool discard it) and borrow again.

Coroutine context matters. Calling Redis::get() directly (single connection) inside a Swoole coroutine blocks the entire worker, not just the coroutine. If you're in a coroutine runtime, always go through the pool, even for "just one quick call".

Pool sizing. maxOpen should be tuned against your worker count and Redis maxclients, not arbitrarily high. A rule of thumb: maxOpen ≈ expected concurrent in-flight Redis operations per worker × 1.2. Oversizing hides the real problem (slow commands) and pushes it onto Redis.

Testing. The package talks raw TCP, so a mock Redis class implementing your RedisInterface is enough for unit tests; reserve a real Redis (docker-compose) for integration tests, and make sure to flushdb on a non-default DB (e.g. database: 15) so you don't wipe dev data.

Extension point. If you need command X that Redis.php doesn't expose, subclass or wrap it and send the raw command through the underlying connection — the class is thin, so adding methods is cheap and safer than swapping in a different client and losing the multi-runtime support.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo