iprodev/php-easycache
最新稳定版本:v3.0.2
Composer 安装命令:
composer require iprodev/php-easycache
包简介
PHP EasyCache is a lightweight, multi-backend (APCu/Redis/File/PDO) with SWR caching library that speeds up repeated API calls with thread‑safe writes, per‑key TTLs, automatic cleanup, key sanitization, and optional compression.
关键字:
README 文档
README
EasyCache is a pragmatic, batteries‑included cache library that implements the PSR‑16 Simple Cache interface and adds production‑grade features on top:
- 🚀 Multi‑tier storage: APCu, Redis, File, and PDO (MySQL/PostgreSQL/SQLite)
- 🔒 Atomic writes and read locks for file storage
- ⚡ Full SWR (stale‑while‑revalidate + stale‑if‑error), with non‑blocking per‑key locks
- 🔧 Pluggable Serializer & Compressor (PHP/JSON + None/Gzip/Zstd)
- 🔄 Automatic backfill between tiers (e.g., a Redis hit is written back to APCu)
- 🎯 First‑class Laravel integration via a Service Provider & Facade
- ✅ Comprehensive test coverage with PHPUnit
- 🛡️ Improved error handling with detailed logging support
Version: v3.0.1 — Requires PHP 8.1+ and
psr/simple-cache:^3.
📖 Documentation in other languages:
📦 Installation
composer require iprodev/php-easycache
Optional dependencies
ext-apcufor the APCu tierext-redisorpredis/predis:^2.0for the Redis tierext-zlibfor Gzip compressionext-zstdfor Zstd compression
🚀 Quick Start (PSR‑16)
use Iprodev\EasyCache\Cache\MultiTierCache; use Iprodev\EasyCache\Storage\ApcuStorage; use Iprodev\EasyCache\Storage\RedisStorage; use Iprodev\EasyCache\Storage\FileStorage; use Iprodev\EasyCache\Serialization\NativeSerializer; use Iprodev\EasyCache\Compression\GzipCompressor; // Tiers: APCu -> Redis -> File $apcu = new ApcuStorage('ec:'); // phpredis (example); predis is also supported $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $redisStore = new RedisStorage($redis, 'ec:'); $file = new FileStorage(__DIR__.'/cache'); $cache = new MultiTierCache( [$apcu, $redisStore, $file], new NativeSerializer(), new GzipCompressor(3), defaultTtl: 600 ); // PSR-16 API $cache->set('user_42', ['id'=>42, 'name'=>'Ava'], 300); $data = $cache->get('user_42'); // ['id'=>42, 'name'=>'Ava']
🎯 Core Features
1. Multi-Tier Caching
Organize your cache in tiers from fastest to slowest. The library automatically:
- Reads from the fastest available tier
- Writes to all tiers
- Backfills faster tiers when data is found in slower tiers
// Example: Memory -> Redis -> Database $cache = new MultiTierCache( [ new ApcuStorage('app:'), // Fast: In-memory new RedisStorage($redis), // Medium: Network cache new PdoStorage($pdo, 'cache') // Slow: Database fallback ], new NativeSerializer(), new NullCompressor(), 3600 // 1 hour default TTL );
2. Stale-While-Revalidate (SWR)
When data expires but is still inside the SWR window, stale data is served instantly while a refresh happens in the background. This prevents cache stampedes and ensures fast response times.
$result = $cache->getOrSetSWR( key: 'posts_homepage', producer: function () { // Expensive API call or database query return fetchPostsFromDatabase(); }, ttl: 300, // 5 minutes of fresh data swrSeconds: 120, // Serve stale up to 2 minutes after expiry staleIfErrorSeconds: 600, // If refresh fails, serve stale up to 10 minutes options: ['mode' => 'defer'] // Defer refresh until after response );
How it works:
- If data is fresh, it's returned immediately
- If data is expired but within SWR window:
- Stale data is returned instantly
- Background refresh is triggered (non-blocking)
- If refresh fails, stale data continues to be served (within staleIfError window)
3. Pluggable Serialization
Choose the serializer that fits your needs:
// PHP Native Serializer (supports objects) use Iprodev\EasyCache\Serialization\NativeSerializer; $cache = new MultiTierCache([$storage], new NativeSerializer()); // JSON Serializer (portable, faster for simple data) use Iprodev\EasyCache\Serialization\JsonSerializer; $cache = new MultiTierCache([$storage], new JsonSerializer());
4. Pluggable Compression
Save memory and disk space:
// No compression use Iprodev\EasyCache\Compression\NullCompressor; $cache = new MultiTierCache([$storage], $serializer, new NullCompressor()); // Gzip compression (balanced) use Iprodev\EasyCache\Compression\GzipCompressor; $cache = new MultiTierCache([$storage], $serializer, new GzipCompressor(5)); // Zstd compression (fastest) use Iprodev\EasyCache\Compression\ZstdCompressor; $cache = new MultiTierCache([$storage], $serializer, new ZstdCompressor(3));
💾 Storage Backends
APCu Storage
Fast in-memory cache, perfect as the first tier.
use Iprodev\EasyCache\Storage\ApcuStorage; $storage = new ApcuStorage( prefix: 'myapp:' // Namespace your keys );
Features:
- Lightning-fast memory access
- Shared between PHP-FPM workers
- Automatic expiration
- Safe clear() that only deletes prefixed keys
Redis Storage
Network-based cache with persistence options.
use Iprodev\EasyCache\Storage\RedisStorage; // Using phpredis $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $storage = new RedisStorage($redis, 'myapp:'); // Using predis $redis = new Predis\Client('tcp://127.0.0.1:6379'); $storage = new RedisStorage($redis, 'myapp:');
Features:
- Works with phpredis or predis
- TTL support with SETEX
- Safe clear() with prefix scanning
- Automatic expiration
File Storage
Reliable disk-based cache with sharding.
use Iprodev\EasyCache\Storage\FileStorage; $storage = new FileStorage( path: '/var/cache/myapp', // Cache directory ext: '.cache', // File extension shards: 2 // Directory sharding level (0-3) );
Features:
- Atomic writes (temp file + rename)
- Read locks with flock()
- Directory sharding for performance
- Configurable file extension
Directory Sharding Example:
With shards=2, key "user_123" (hash: a1b2c3d4):
/var/cache/myapp/a1/b2/a1b2c3d4.cache
PDO Storage
SQL database cache for shared environments.
use Iprodev\EasyCache\Storage\PdoStorage; $pdo = new PDO('mysql:host=localhost;dbname=cache', 'user', 'pass'); $storage = new PdoStorage($pdo, 'easycache'); // Create table (run once during setup) $storage->ensureTable();
Supported databases:
- SQLite:
sqlite:/path/to/cache.db - MySQL:
mysql:host=localhost;dbname=cache - PostgreSQL:
pgsql:host=localhost;dbname=cache
Features:
- TTL support with expiration check
- Prune expired items with
prune() - UPSERT support (INSERT ... ON CONFLICT)
- Indexed queries for performance
🎨 Complete Examples
Example 1: Simple File Cache
use Iprodev\EasyCache\Cache\MultiTierCache; use Iprodev\EasyCache\Storage\FileStorage; use Iprodev\EasyCache\Serialization\NativeSerializer; use Iprodev\EasyCache\Compression\NullCompressor; $storage = new FileStorage(__DIR__ . '/cache'); $cache = new MultiTierCache([$storage], new NativeSerializer(), new NullCompressor()); // Set with 1 hour TTL $cache->set('user_profile', [ 'id' => 123, 'name' => 'John Doe', 'email' => 'john@example.com' ], 3600); // Get $profile = $cache->get('user_profile'); // Check existence if ($cache->has('user_profile')) { echo "Profile is cached!"; } // Delete $cache->delete('user_profile');
Example 2: Multi-Tier with Backfill
// Setup: APCu (fast) -> Redis (medium) -> File (slow) $apcu = new ApcuStorage('app:'); $redis = new RedisStorage($redisClient, 'app:'); $file = new FileStorage('/var/cache/app'); $cache = new MultiTierCache([$apcu, $redis, $file]); // First request: Cache miss, data fetched and stored in all tiers $data = $cache->get('expensive_data'); // APCu crashes and restarts... // Next request: Data found in Redis, automatically backfilled to APCu $data = $cache->get('expensive_data'); // Fast!
Example 3: SWR for API Responses
use Psr\Log\LoggerInterface; $cache = new MultiTierCache( [$apcu, $redis], new NativeSerializer(), new GzipCompressor(5), 600, // 10 min default TTL $logger // Optional PSR-3 logger ); $posts = $cache->getOrSetSWR( key: 'api_posts_latest', producer: function() use ($apiClient) { // This is expensive return $apiClient->fetchPosts(); }, ttl: 300, // Fresh for 5 minutes swrSeconds: 60, // Serve stale for 1 minute while refreshing staleIfErrorSeconds: 300, // Serve stale for 5 minutes if API fails options: ['mode' => 'defer'] // Refresh after response sent );
Example 4: Batch Operations
// Set multiple $cache->setMultiple([ 'key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3', ], 3600); // Get multiple with default $results = $cache->getMultiple(['key1', 'key2', 'missing'], 'default'); // ['key1' => 'value1', 'key2' => 'value2', 'missing' => 'default'] // Delete multiple $cache->deleteMultiple(['key1', 'key2']);
Example 5: DateInterval TTL
// Cache for 2 hours $cache->set('key', 'value', new DateInterval('PT2H')); // Cache for 1 day $cache->set('key', 'value', new DateInterval('P1D')); // Cache for 30 days $cache->set('key', 'value', new DateInterval('P30D'));
Example 6: Custom Logger Integration
use Monolog\Logger; use Monolog\Handler\StreamHandler; $logger = new Logger('cache'); $logger->pushHandler(new StreamHandler('/var/log/cache.log', Logger::WARNING)); $cache = new MultiTierCache( [$storage], new NativeSerializer(), new NullCompressor(), 3600, $logger // Will log warnings and errors );
Example 7: Scheduled Cleanup
// Run this in a cron job or scheduled task $pruned = $cache->prune(); echo "Pruned {$pruned} expired items"; // For PDO storage, this removes expired rows // For File/APCu/Redis, expiration is automatic
🎭 Laravel Integration
Setup
- Install the package:
composer require iprodev/php-easycache
- Publish configuration:
php artisan vendor:publish --tag=easycache-config
- Configure in
config/easycache.php:
return [ 'drivers' => ['apcu', 'redis', 'file'], 'default_ttl' => 600, 'serializer' => [ 'driver' => 'php', // php|json ], 'compressor' => [ 'driver' => 'gzip', // none|gzip|zstd 'level' => 5, ], 'redis' => [ 'host' => env('REDIS_HOST', '127.0.0.1'), 'port' => env('REDIS_PORT', 6379), 'password' => env('REDIS_PASSWORD', null), 'database' => env('REDIS_CACHE_DB', 1), ], ];
Using the Facade
use EasyCache; // Simple operations EasyCache::set('user_settings', $settings, 3600); $settings = EasyCache::get('user_settings'); // SWR pattern $data = EasyCache::getOrSetSWR( 'dashboard_stats', fn() => $this->computeStats(), 300, // Fresh for 5 min 60, // SWR for 1 min 300 // Stale-if-error for 5 min ); // Batch operations EasyCache::setMultiple([ 'key1' => 'value1', 'key2' => 'value2', ]);
Artisan Commands
The package includes a prune command:
# Prune expired cache items php artisan easycache:prune # Add to your scheduler (app/Console/Kernel.php) $schedule->command('easycache:prune')->daily();
🔑 Key Rules (PSR‑16)
- Allowed characters:
[A-Za-z0-9_.] - Max length: 64 characters
- Reserved characters (not allowed):
{ } ( ) / \ @ :
// Valid keys $cache->set('user_123', $data); $cache->set('posts.latest', $data); $cache->set('CamelCase', $data); // Invalid keys (will throw InvalidArgument exception) $cache->set('user:123', $data); // Contains : $cache->set('user/123', $data); // Contains / $cache->set('user@123', $data); // Contains @ $cache->set(str_repeat('x', 65), $data); // Too long
🧪 Testing & Quality Assurance
Running Tests
# Run all tests composer test # Run with coverage composer test -- --coverage-html coverage # Run specific test suite ./vendor/bin/phpunit --testsuite "Storage Tests"
Static Analysis
# Run PHPStan composer stan # Check coding standards composer cs # Fix coding standards automatically composer cs:fix
Test Coverage
The library includes comprehensive tests for:
- ✅ All storage backends (File, APCu, Redis, PDO)
- ✅ Multi-tier caching with backfill
- ✅ SWR functionality
- ✅ Serializers (Native, JSON)
- ✅ Compressors (Null, Gzip, Zstd)
- ✅ Key validation
- ✅ Lock mechanism
- ✅ Edge cases and error handling
🔧 Advanced Configuration
Custom Lock Path
$cache = new MultiTierCache( [$storage], $serializer, $compressor, 3600, $logger, '/custom/lock/path' // Custom lock directory );
File Storage Sharding Levels
// No sharding: /cache/md5hash.cache $storage = new FileStorage('/cache', '.cache', 0); // 1 level: /cache/a1/md5hash.cache $storage = new FileStorage('/cache', '.cache', 1); // 2 levels: /cache/a1/b2/md5hash.cache (recommended) $storage = new FileStorage('/cache', '.cache', 2); // 3 levels: /cache/a1/b2/c3/md5hash.cache $storage = new FileStorage('/cache', '.cache', 3);
Environment Variables (Laravel)
# .env file EASYCACHE_DRIVER=redis EASYCACHE_REDIS_HOST=127.0.0.1 EASYCACHE_REDIS_PORT=6379 EASYCACHE_REDIS_PASSWORD=secret EASYCACHE_REDIS_DB=1 EASYCACHE_DEFAULT_TTL=600
🚨 Error Handling
All storage operations are wrapped with proper error handling. Failures are logged (if logger is provided) and don't crash your application:
use Monolog\Logger; $logger = new Logger('cache'); $cache = new MultiTierCache([$storage], $serializer, $compressor, 3600, $logger); // If storage fails, operation returns false but doesn't throw $result = $cache->set('key', 'value'); if (!$result) { // Check logs for details echo "Cache set failed, check logs"; }
Logged Events:
- Storage read/write failures
- Compression/decompression errors
- Lock acquisition failures
- SWR refresh errors
- Serialization errors
🔄 Backwards Compatibility
For projects upgrading from v2, use the BC wrapper:
use Iprodev\EasyCache\EasyCache; $cache = new EasyCache([ 'cache_path' => __DIR__ . '/cache', 'cache_extension' => '.cache', 'cache_time' => 3600, 'directory_shards' => 2, ]); // Works like v2 $cache->set('key', 'value'); $value = $cache->get('key');
📝 Best Practices
- Use multi-tier wisely: APCu → Redis → File/PDO
- Set appropriate TTLs: Balance freshness vs. performance
- Use SWR for expensive operations: Prevent cache stampedes
- Monitor cache hit rates: Use logging to track performance
- Schedule pruning: For PDO storage, prune regularly
- Use compression for large data: GzipCompressor or ZstdCompressor
- Namespace your keys: Use prefixes to avoid collisions
- Test error scenarios: Ensure your app handles cache failures gracefully
🤝 Contributing
Contributions are welcome! Please see CONTRIBUTING.md for details.
Development Setup
git clone https://github.com/iprodev/php-easycache.git cd php-easycache composer install composer test
📄 License
MIT © iprodev
🔗 Links
- Documentation
- API Reference - English
- API Reference - فارسی
- API Reference - کوردی
- Examples - English
- Examples - فارسی
- Examples - کوردی
- Changelog
- Security Policy
- Code of Conduct
💬 Support
- 📧 Email: support@iprodev.com
- 🐛 Issues: GitHub Issues
- 💡 Discussions: GitHub Discussions
统计信息
- 总下载量: 462
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 11
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2015-07-06