symfony/polyfill-apcu
Symfony Polyfill for APCu: provides apcu_* functions and the APCuIterator class for projects relying on the legacy APC extension, enabling compatible caching APIs when APCu isn’t available or APC is used.
Installation:
composer require symfony/polyfill-apcu
Add to composer.json under require:
"symfony/polyfill-apcu": "^1.35"
First Use Case:
Enable the polyfill in your AppServiceProvider:
use Symfony\Polyfill\Apcu\ApcuFunctions;
public function boot()
{
if (!extension_loaded('apcu')) {
ApcuFunctions::register();
\Log::info('APCu polyfill activated (non-native environment)');
}
}
Verify Functionality: Test with Laravel’s cache facade:
Cache::put('test_key', 'test_value', 60);
$value = Cache::get('test_key');
config/cache.php for apcu driver usage.config/session.php for apcu driver.apcu_* Calls: Audit your codebase for direct apcu_* function usage.Environment-Aware Activation:
Use .env to control polyfill activation:
APCU_POLYFILL=true
Then in AppServiceProvider:
if (env('APCU_POLYFILL', false) && !extension_loaded('apcu')) {
ApcuFunctions::register();
}
Laravel Cache Store Integration:
Extend the ApcuStore to log polyfill usage:
Cache::extend('apcu', function ($app) {
return new class($app['cache.store.apcu'], $app) extends \Illuminate\Cache\ApcuStore {
public function store($key, $value, $ttl = null)
{
if (ApcuFunctions::isRegistered()) {
\Log::debug("APCu polyfill storing: {$key}");
}
return parent::store($key, $value, $ttl);
}
};
});
Fallback Mechanism: Implement a fallback cache driver if polyfill fails:
Cache::extend('apcu_fallback', function ($app) {
return new class($app) extends \Illuminate\Cache\Repository {
public function store($key, $value, $ttl = null)
{
try {
if (ApcuFunctions::isRegistered()) {
return Cache::store('apcu')->store($key, $value, $ttl);
}
} catch (\Exception $e) {
\Log::error("APCu polyfill failed: " . $e->getMessage());
}
return Cache::store('file')->store($key, $value, $ttl);
}
};
});
Shared Hosting Workflow:
.env for shared hosting environments.apcu cache driver for non-critical operations (e.g., sessions, config).Local Development Workflow:
ext-apcu is unavailable.APCU_POLYFILL=true in .env for consistency with production.Migration Workflow:
Laravel Events: Listen to CacheStoredEvent to log polyfill usage:
Cache::store('apcu')->extend(function ($store) {
$store->listen(function ($event) {
if (ApcuFunctions::isRegistered()) {
\Log::debug("Polyfill stored: {$event->key}");
}
});
});
Testing: Mock polyfill in PHPUnit:
public function testApcuPolyfill()
{
if (!extension_loaded('apcu')) {
ApcuFunctions::register();
}
Cache::put('test', 'value');
$this->assertEquals('value', Cache::get('test'));
}
Performance Isolation: Restrict polyfill to non-critical cache stores:
// config/cache.php
'stores' => [
'apcu' => [
'driver' => 'apcu',
'use_polyfill' => env('APCU_POLYFILL', false),
],
'redis' => ['driver' => 'redis'], // Keep critical paths on Redis
],
Performance Overhead:
Memory Limits:
Allowed memory exhausted for large caches.memory_get_usage() and limit cache size.Serialization Issues:
serialize()/unserialize() or fall back to file cache:
Cache::store('file')->put($key, serialize($value));
Unsupported Functions:
apcu_* functions (e.g., apcu_cas(), apcu_delete()) may not be fully supported.grep -r "apcu_" and refactor unsupported calls.Process-Local Scope:
Opcode Caching:
opcache functionality.opcache is enabled natively if needed.Check Polyfill Activation:
if (ApcuFunctions::isRegistered()) {
\Log::info('APCu polyfill is active');
}
Log Cache Operations:
Cache::store('apcu')->extend(function ($store) {
$store->listen(function ($event) {
\Log::debug("Cache event: {$event->key} - {$event->action}");
});
});
Memory Usage:
$memory = memory_get_usage(true);
\Log::info("APCu polyfill memory usage: " . ($memory / 1024 / 1024) . "MB");
Environment-Specific Configuration:
Use .env to toggle polyfill:
# .env.shared-hosting
APCU_POLYFILL=true
Fallback Cache Driver: Implement a hybrid driver:
Cache::extend('hybrid', function ($app) {
return new class($app) extends \Illuminate\Cache\Repository {
public function store($key, $value, $ttl = null)
{
try {
if (ApcuFunctions::isRegistered()) {
return Cache::store('apcu')->store($key, $value, $ttl);
}
} catch (\Exception $e) {
\Log::error("APCu polyfill failed: " . $e->getMessage());
}
return Cache::store('redis')->store($key, $value, $ttl);
}
};
});
Monitor Polyfill Usage: Track polyfill activation in production:
if (ApcuFunctions::isRegistered()) {
\App\Models\CacheLog::create([
'driver' => 'apcu_polyfill',
'ip' => request()->ip(),
]);
}
Benchmark Before/After: Compare performance with native APCu:
$start = microtime(true);
Cache::put('benchmark', 'value');
$time = microtime(true) - $start;
\Log::info("Cache operation time: {$time}s");
Cleanup Old Data: Polyfill may retain stale data. Clear cache periodically:
if (ApcuFunctions::isRegistered()) {
ApcuFunctions::apcu_clear_cache();
}
Avoid in CI/CD:
Disable polyfill in CI/CD pipelines where ext-apcu is available:
# .env.ci
APCU_POLYFILL=false
How can I help you explore Laravel packages today?