julienlinard / php-cache
Système de cache moderne et sécurisé pour PHP 8+ avec support de multiples drivers (File, Redis, Memcached, Array), tags, TTL et invalidation
Installs: 160
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/julienlinard/php-cache
Requires
- php: >=8.0
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^11.5
Suggests
- ext-memcached: Pour utiliser le driver Memcached
- ext-redis: Pour utiliser le driver Redis
README
🇫🇷 Read in French | 🇬🇧 Read in English
💝 Support the project
If this package is useful to you, consider becoming a sponsor to support the development and maintenance of this open source project.
A modern and secure caching system for PHP 8+ with support for multiple drivers (File, Redis, Memcached, Array), tags, TTL, and invalidation.
🚀 Installation
composer require julienlinard/php-cache
Requirements: PHP 8.0 or higher
⚡ Quick Start
Basic Configuration
<?php require_once __DIR__ . '/vendor/autoload.php'; use JulienLinard\Cache\Cache; // Initialize with configuration Cache::init([ 'default' => 'file', // or 'array', 'redis' 'drivers' => [ 'array' => [], 'file' => [ 'path' => __DIR__ . '/cache', 'ttl' => 3600, // Default TTL in seconds ], 'redis' => [ 'host' => '127.0.0.1', 'port' => 6379, 'password' => null, 'database' => 0, ], ], ]); // Simple usage Cache::set('user_123', ['name' => 'John', 'email' => 'john@example.com'], 3600); $user = Cache::get('user_123');
📋 Features
- ✅ Multiple Drivers: Array, File, Redis
- ✅ TTL (Time To Live): Automatic entry expiration
- ✅ Tags: Tag system for grouped invalidation
- ✅ Security: Key validation, protection against injections
- ✅ Secure Serialization: JSON usage with validation
- ✅ Multiple Operations: getMultiple, setMultiple, deleteMultiple
- ✅ Increment/Decrement: Support for numeric values
- ✅ Fluid Interface: Simple and intuitive API
📖 Documentation
Available Drivers
Array Driver (Memory)
The Array driver stores data in memory. Useful for testing and development.
use JulienLinard\Cache\Cache; Cache::init([ 'default' => 'array', 'drivers' => [ 'array' => [ 'prefix' => 'myapp', // Optional prefix for all keys 'ttl' => 3600, // Default TTL ], ], ]);
File Driver (Disk)
The File driver stores data in files on the filesystem.
Cache::init([ 'default' => 'file', 'drivers' => [ 'file' => [ 'path' => __DIR__ . '/cache', // Cache directory 'prefix' => 'myapp', 'ttl' => 3600, 'file_permissions' => 0644, // File permissions 'directory_permissions' => 0755, // Directory permissions ], ], ]);
Redis Driver
The Redis driver requires the PHP Redis extension.
# Install Redis extension
pecl install redis
Cache::init([ 'default' => 'redis', 'drivers' => [ 'redis' => [ 'host' => '127.0.0.1', 'port' => 6379, 'password' => 'your_password', // Optional 'database' => 0, 'timeout' => 2.0, 'persistent' => false, // Persistent connection 'persistent_id' => null, 'prefix' => 'myapp', 'ttl' => 3600, ], ], ]);
Basic Operations
Store a Value
// With default TTL Cache::set('key', 'value'); // With custom TTL (in seconds) Cache::set('key', 'value', 3600); // Complex data Cache::set('user', [ 'id' => 123, 'name' => 'John', 'email' => 'john@example.com', ], 3600);
Retrieve a Value
// Simple retrieval $value = Cache::get('key'); // With default value $value = Cache::get('key', 'default_value'); // Complex data $user = Cache::get('user', []);
Check Existence
if (Cache::has('key')) { // Key exists }
Delete a Value
Cache::delete('key');
Clear All Cache
Cache::clear();
Multiple Operations
Retrieve Multiple Values
$values = Cache::getMultiple(['key1', 'key2', 'key3'], null); // Returns: ['key1' => value1, 'key2' => value2, 'key3' => value3]
Store Multiple Values
Cache::setMultiple([ 'key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3', ], 3600); // Common TTL for all keys
Delete Multiple Keys
$deleted = Cache::deleteMultiple(['key1', 'key2', 'key3']); // Returns the number of deleted keys
Increment and Decrement
// Increment Cache::set('counter', 0); Cache::increment('counter'); // 1 Cache::increment('counter', 5); // 6 // Decrement Cache::decrement('counter'); // 5 Cache::decrement('counter', 2); // 3
Pull (Retrieve and Delete)
$value = Cache::pull('key'); // Retrieves and deletes in one operation
Using a Specific Driver
// Use a specific driver Cache::set('key', 'value', 3600, 'redis'); $value = Cache::get('key', null, 'redis'); // Or get the driver directly $redisCache = Cache::driver('redis'); $redisCache->set('key', 'value');
Tag System
Tags allow grouping cache entries and invalidating them together.
// Create a tagged cache $taggedCache = Cache::tags(['users', 'posts']); // Store values with tags $taggedCache->set('user_1', ['name' => 'John']); $taggedCache->set('user_2', ['name' => 'Jane']); // Get keys associated with a tag $keys = $taggedCache->getKeysByTag('users'); // Invalidate all keys with a tag $taggedCache->invalidateTags('users'); // or multiple tags $taggedCache->invalidateTags(['users', 'posts']);
Advanced Usage with CacheManager
use JulienLinard\Cache\CacheManager; $manager = CacheManager::getInstance([ 'default' => 'file', 'drivers' => [ 'file' => ['path' => __DIR__ . '/cache'], ], ]); // Get a driver $driver = $manager->driver('file'); // Register a custom driver $customDriver = new MyCustomDriver(); $manager->registerDriver('custom', $customDriver); // Change the default driver $manager->setDefaultDriver('redis');
Key Validation
The system automatically validates keys for security:
- ✅ Allowed characters: letters, numbers,
_,-,. - ✅ Maximum length: 250 characters
- ✅ Protection against path injections (
..,/,\)
use JulienLinard\Cache\KeyValidator; // Validate a key try { KeyValidator::validate('valid_key_123'); } catch (InvalidKeyException $e) { // Invalid key } // Sanitize a key $cleanKey = KeyValidator::sanitize('invalid/key@test'); // Returns: 'invalid_key_test'
Error Handling
use JulienLinard\Cache\Exceptions\CacheException; use JulienLinard\Cache\Exceptions\InvalidKeyException; use JulienLinard\Cache\Exceptions\DriverException; try { Cache::set('key', 'value'); } catch (InvalidKeyException $e) { // Invalid key } catch (DriverException $e) { // Driver error } catch (CacheException $e) { // Other cache error }
🔒 Security
Implemented Security Measures
- Key Validation: Protection against path injections
- Secure Serialization: JSON usage with strict validation
- File Permissions: Permission control for File driver
- Atomic Writing: File driver uses temporary files to prevent corruption
- Input Validation: All entries are validated before storage
Best Practices
// ✅ GOOD: Simple and descriptive keys Cache::set('user_123', $userData); // ❌ BAD: Keys with special characters Cache::set('user/123', $userData); // Throws exception // ✅ GOOD: Use prefixes Cache::init([ 'drivers' => [ 'file' => ['prefix' => 'myapp'], ], ]); // ✅ GOOD: Validate data before caching $data = validateAndSanitize($userInput); Cache::set('key', $data);
🧪 Tests
# Run tests composer test # With code coverage composer test-coverage
📝 Usage Examples
Database Query Caching
use JulienLinard\Cache\Cache; function getUser(int $id): array { $cacheKey = "user_{$id}"; // Check cache if (Cache::has($cacheKey)) { return Cache::get($cacheKey); } // Retrieve from database $user = fetchUserFromDatabase($id); // Cache for 1 hour Cache::set($cacheKey, $user, 3600); return $user; }
Cache with Tag Invalidation
use JulienLinard\Cache\Cache; // Store users with tag $usersCache = Cache::tags('users'); $usersCache->set('user_1', $user1, 3600); $usersCache->set('user_2', $user2, 3600); // When a user is updated, invalidate the tag function updateUser(int $id, array $data): void { // Update in database updateUserInDatabase($id, $data); // Invalidate all entries with 'users' tag $usersCache = Cache::tags('users'); $usersCache->invalidateTags('users'); }
View/Template Caching
use JulienLinard\Cache\Cache; function renderView(string $template, array $data): string { $cacheKey = 'view_' . md5($template . serialize($data)); if (Cache::has($cacheKey)) { return Cache::get($cacheKey); } $html = renderTemplate($template, $data); Cache::set($cacheKey, $html, 1800); // 30 minutes return $html; }
Counter with Expiration
use JulienLinard\Cache\Cache; function incrementPageViews(string $pageId): int { $key = "page_views_{$pageId}"; if (!Cache::has($key)) { // Initialize with 24h expiration Cache::set($key, 0, 86400); } return Cache::increment($key); }
🤝 Integration with Other Packages
With doctrine-php
use JulienLinard\Cache\Cache; use JulienLinard\Doctrine\EntityManager; function getCachedEntity(EntityManager $em, string $entityClass, int $id): ?object { $cacheKey = strtolower($entityClass) . "_{$id}"; if (Cache::has($cacheKey)) { $data = Cache::get($cacheKey); // Rebuild entity from data return $em->getRepository($entityClass)->find($id); } $entity = $em->getRepository($entityClass)->find($id); if ($entity) { // Store entity data Cache::set($cacheKey, $entity->toArray(), 3600); } return $entity; }
📚 API Reference
Cache (Facade)
Cache::init(array $config): Initialize the managerCache::get(string $key, mixed $default = null, ?string $driver = null): Retrieve a valueCache::set(string $key, mixed $value, ?int $ttl = null, ?string $driver = null): Store a valueCache::has(string $key, ?string $driver = null): Check existenceCache::delete(string $key, ?string $driver = null): Delete a valueCache::clear(?string $driver = null): Clear cacheCache::increment(string $key, int $value = 1, ?string $driver = null): IncrementCache::decrement(string $key, int $value = 1, ?string $driver = null): DecrementCache::pull(string $key, mixed $default = null, ?string $driver = null): Retrieve and deleteCache::tags(string|array $tags, ?string $driver = null): Create tagged cacheCache::driver(?string $driver = null): Get a driver
CacheInterface
All drivers implement CacheInterface with the following methods:
get(string $key, mixed $default = null): mixedset(string $key, mixed $value, ?int $ttl = null): booldelete(string $key): boolhas(string $key): boolclear(): boolgetMultiple(array $keys, mixed $default = null): arraysetMultiple(array $values, ?int $ttl = null): booldeleteMultiple(array $keys): intincrement(string $key, int $value = 1): int|falsedecrement(string $key, int $value = 1): int|falsepull(string $key, mixed $default = null): mixed
🐛 Troubleshooting
File Driver Not Working
Check that the cache directory exists and is writable:
$cachePath = __DIR__ . '/cache'; if (!is_dir($cachePath)) { mkdir($cachePath, 0755, true); }
Redis Driver Not Connecting
- Check that Redis extension is installed:
php -m | grep redis - Check that Redis is running:
redis-cli ping - Check connection parameters in configuration
"Invalid Key" Error
Keys must follow this format:
- Allowed characters:
a-z,A-Z,0-9,_,-,. - Maximum length: 250 characters
- No relative paths (
..,/,\)
📝 License
MIT License - See the LICENSE file for more details.
🤝 Contributing
Contributions are welcome! Feel free to open an issue or a pull request.
📧 Support
For any questions or issues, please open an issue on GitHub.
💝 Support the project
If this package is useful to you, consider becoming a sponsor to support the development and maintenance of this open source project.
Developed with ❤️ by Julien Linard