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

Memcached Bundle Laravel Package

mcfedr/memcached-bundle

Symfony bundle that integrates the Memcached PHP extension, providing a preconfigured Memcached client service and simple bundle configuration. Drop it into your Symfony app to use Memcached for caching or sessions without boilerplate wiring.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install the extension and the bundle

# The PHP extension is a hard requirement; the bundle is only a thin wrapper
pecl install memcached          # needs libmemcached-dev on the host
composer require mcfedr/memcached-bundle

2. Register the bundle (the package predates Symfony 4 Flex recipes, so this is manual)

// app/AppKernel.php  (Symfony 2.x/3.x)
$bundles = [
    // ...
    new Mcfedr\MemcachedBundle\McfedrMemcachedBundle(),
];
// config/bundles.php  (Symfony 3.4+/4.x if you are retrofitting)
return [
    // ...
    Mcfedr\MemcachedBundle\McfedrMemcachedBundle::class => ['all' => true],
];

3. Point it at your servers

# app/config/config.yml
mcfedr_memcached:
    servers:
        - { host: 127.0.0.1, port: 11211 }
    # add more entries for a pool; the extension handles consistent hashing

Because this is a tiny bundle, the Configuration class is short — open src/DependencyInjection/Configuration.php in vendor/mcfedr/memcached-bundle first to see the exact keys for your pinned version (options like persistent_id, options, and servers have shifted between releases). Do not trust blog posts for this; read the class.

4. Find the service and prove it works

bin/console debug:container | grep -i memcached
# then, in a controller or a console command:
$mc = $this->get('mcfedr_memcached');   // confirm the id from the step above
$mc->set('smoke_test', 'ok', 60);
var_dump($mc->get('smoke_test'));        // string(2) "ok"

First real use case: replace a database read on a hot, low-churn lookup (settings table, category tree, rendered fragment) with a get/set pair wrapped in a small repository or service. That is the fastest place to see value and the easiest place to add a cache-busting key later.

Implementation Patterns

Wrap it — don't inject \Memcached everywhere. Define one application-facing service that owns key naming, serialization, and TTL defaults. This is what makes the bundle's thinness an advantage rather than a liability.

namespace App\Cache;

class AppCache
{
    private $mc;
    private $prefix;

    public function __construct(\Memcached $mc, string $prefix)
    {
        $this->mc = $mc;
        $this->prefix = $prefix;
    }

    public function remember(string $key, int $ttl, callable $loader)
    {
        $k = $this->prefix . $key;
        $value = $this->mc->get($k);
        if ($this->mc->getResultCode() === \Memcached::RES_SUCCESS) {
            return $value;
        }
        $value = $loader();
        $this->mc->set($k, $value, $ttl);
        return $value;
    }
}
# services.yml
App\Cache\AppCache:
    arguments:
        $mc: '@mcfedr_memcached'      # use the id confirmed via debug:container
        $prefix: '%kernel.environment%.myapp.v1.'

Namespace by environment and schema version. Memcached has no concept of tenants or pools; all apps hitting a server share the keyspace. A prefix of env.app.version. lets you invalidate an entire generation by bumping v1 → v2 in config — the old keys expire on their own TTL.

Bulk reads for list pages. getMulti/setMulti are the single biggest win vs. a naive loop; the round-trip count is what kills you, not the payload.

$keys = array_map(fn($id) => "user.$id", $ids);
$found = $mc->getMulti($keys);          // misses are simply absent
$missing = array_diff($keys, array_keys($found));
// hydrate only $missing from the DB, then setMulti them back

CAS for counter-ish data. The extension gives you cas() and getResultCode() === RES_DATA_EXISTS; use it for lightweight counters or "last writer wins" conflicts instead of reaching for Redis.

Bridge into Symfony's cache layer if the rest of the app already uses PSR-6. Wrap the client in Symfony\Component\Cache\Adapter\MemcachedAdapter::createConnection() — but note this bypasses the bundle entirely, which is often the right call once you are on Symfony 4+.

Warm and invalidate from the console. Put cache warming in a cache:warmup-adjacent command or a post-deploy step; also expose a scoped flush that walks only your prefixed keys (getAllKeys() is available and fine for a small keyspace, dangerous at scale).

Gotchas and Tips

  • This package is unmaintained (last release 2016, zero dependents). Pin it, read the source, and treat the bundle as ~200 lines of DI glue you could vendor yourself. The risk isn't the code — it's that nothing will be fixed when PHP or Symfony moves under it. On Symfony 4.4+/5+, use symfony/cache's MemcachedAdapter instead and keep this only if you're on a legacy branch.
  • The PHP extension, not the bundle, is where failures live. libmemcached version mismatches, missing SASL, and OPT_BINARY_PROTOCOL oddities all surface as generic RES_* codes. When get() returns false, always check getResultCode() before assuming a miss (RES_NOTFOUND vs RES_CONNECTION_SOCKET_CREATE_FAILURE).
  • TTL semantics: values > 30 days (2,592,000 s) are interpreted as a Unix timestamp, not a duration. If you pass a computed time() + $ttl, a small TTL will silently become "expire in 1970" behavior — always pass durations, or branch on the threshold.
  • Key limits: 250 bytes max, no whitespace or control characters. Hashes/URLs/JSON blobs used as keys will fail intermittently and look like random misses. Hash long keys (sha1($key)) or store a lookup.
  • Serialization is implicit and dangerous across deploys. The extension serializes PHP values automatically; if you cache objects or arrays and then change the class shape, unserialize produces half-populated objects or notices. Cache scalars/arrays, or version your prefix on every deploy that changes the shape.
  • No tags, no atomic multi-key invalidation, no pub/sub. Design around version keys or short TTLs; don't try to bolt tag invalidation onto this.
  • flush() is per-server, immediate, and nukes everyone's data — including other apps on the same memcached. Never wire it to a user-facing "clear cache" button without a prefix check.
  • Configure options at boot, not per-call. OPT_DISTRIBUTION (consistent hashing), OPT_LIBKETAMA_COMPATIBLE, OPT_PREFIX_KEY, and timeouts belong in the bundle config / a compiler pass so every request gets the same behavior. Per-call setOption on a persistent connection is a classic source of "works in dev, wrong server in prod."
  • Persistent connections + persistent_id can leak state across requests in PHP-FPM workers; if you see stale values after a deploy, unset the persistent id first.
  • Autowiring: alias the concrete service to the \Memcached type in your services.yml (\Memcached: '@mcfedr_memcached') so constructor injection works without remembering the string id.
  • Testing: the extension has no in-memory fake. Inject an interface (as in the wrapper above) and bind a no-op / array-backed implementation in test, otherwise unit tests require a live daemon and fail on CI.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route