Weave Code
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

Cache Plugin Laravel Package

php-http/cache-plugin

PSR-6 cache plugin for HTTPlug that adds transparent HTTP response caching to your client. Plug it into the HTTPlug plugin client to cache and reuse responses, reducing network calls and improving performance.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup in Laravel

  1. Install the package and a PSR-6 cache adapter (e.g., Redis or Symfony Cache):

    composer require php-http/cache-plugin symfony/cache symfony/redis-cache
    
  2. Configure a PSR-6 cache pool (e.g., in config/cache.php or a service provider):

    use Symfony\Component\Cache\Adapter\RedisAdapter;
    
    $cachePool = RedisAdapter::createConnection('redis://localhost:6379')->getPool();
    
  3. Wrap your HTTPlug client (e.g., Guzzle or Symfony HttpClient) with the CachePlugin:

    use Http\Client\Common\Plugin\CachePlugin;
    use Http\Client\Common\Plugin\CachePlugin\CacheKeyGenerator\SimpleGenerator;
    use Http\Client\Common\Plugin\CachePlugin\CachePolicy;
    
    $client = new \Http\Client\Common\Plugin\Client\BaseClient();
    $cachePlugin = new CachePlugin(
        $cachePool,
        new SimpleGenerator(),
        new CachePolicy() // Customize TTL, blacklisted paths, etc.
    );
    $cachedClient = $cachePlugin->attachTo($client);
    
  4. First use case: Cache API responses for a Laravel service:

    $response = $cachedClient->sendRequest(new \Http\Message\Request('GET', 'https://api.example.com/data'));
    $data = json_decode($response->getBody(), true);
    

Implementation Patterns

1. Decorator-Based Integration

  • Workflow: Attach the CachePlugin to any HTTPlug-compatible client (e.g., Guzzle, Symfony HttpClient) without modifying existing logic.
  • Example:
    // Laravel service provider
    public function register()
    {
        $this->app->singleton('http.cached_client', function ($app) {
            $client = $app->make(\Http\Client\Common\Plugin\Client\BaseClient::class);
            $cachePool = $app->make(\Psr\Cache\CacheItemPoolInterface::class);
            return (new CachePlugin($cachePool))
                ->attachTo($client);
        });
    }
    

2. Custom Cache Key Generation

  • Use case: Override default key generation (e.g., include query params or headers).
  • Implementation:
    use Http\Client\Common\Plugin\CachePlugin\CacheKeyGenerator\HeaderCacheKeyGenerator;
    
    $cachePlugin = new CachePlugin(
        $cachePool,
        new HeaderCacheKeyGenerator(['Authorization']) // Include auth headers in cache key
    );
    

3. Selective Caching with Blacklists

  • Use case: Exclude specific endpoints (e.g., /auth, /webhooks) from caching.
  • Config:
    $cachePolicy = new CachePolicy();
    $cachePolicy->setBlacklistedPaths(['/auth', '/webhooks']);
    $cachePlugin = new CachePlugin($cachePool, new SimpleGenerator(), $cachePolicy);
    

4. Cache Listeners for Debugging

  • Use case: Log cache hits/misses or add headers (e.g., X-Cache: HIT).
  • Implementation:
    use Http\Client\Common\Plugin\CachePlugin\CacheListener\AddHeaderCacheListener;
    
    $listener = new AddHeaderCacheListener();
    $cachePlugin = new CachePlugin($cachePool, new SimpleGenerator(), [], [$listener]);
    

5. ETag-Specific Caching

  • Use case: Cache only responses with ETag headers (e.g., for immutable resources).
  • Implementation:
    use Http\Client\Common\Plugin\CachePlugin\EtagCachePlugin;
    
    $etagCachePlugin = new EtagCachePlugin($cachePool);
    $cachedClient = $etagCachePlugin->attachTo($client);
    

6. Laravel-Specific: Cache Invalidation

  • Use case: Invalidate cache when related data changes (e.g., after model updates).
  • Implementation:
    use Psr\Cache\CacheItemPoolInterface;
    
    public function update()
    {
        // Business logic...
        $this->invalidateCache();
    }
    
    protected function invalidateCache()
    {
        $cachePool = app(CacheItemPoolInterface::class);
        $cachePool->deleteItem('api.example.com/data');
    }
    

Gotchas and Tips

Pitfalls

  1. Stream Detachment Issues:

    • Problem: Serializing PSR-7 responses with attached streams can trigger warnings.
    • Fix: Use CachePlugin::clientCache() or CachePlugin::serverCache() factory methods (v1.3+), which handle stream detachment automatically.
  2. Cache Key Collisions:

    • Problem: Default SimpleGenerator may produce identical keys for similar but distinct requests (e.g., /users?page=1 vs. /users?page=2).
    • Fix: Use HeaderCacheKeyGenerator or extend CacheKeyGenerator to include query params:
      $generator = new class implements CacheKeyGenerator {
          public function generateKey(RequestInterface $request): string
          {
              return sha1($request->getUri() . $request->getBody());
          }
      };
      
  3. TTL Misconfigurations:

    • Problem: Setting default_ttl to null or 0 may lead to unexpected behavior (e.g., infinite cache or no caching).
    • Fix: Explicitly set TTL in seconds (e.g., 3600 for 1 hour):
      $cachePolicy = new CachePolicy();
      $cachePolicy->setDefaultTtl(3600);
      
  4. Blacklist Regex Overlap:

    • Problem: Overlapping regex patterns in blacklisted_paths may block legitimate endpoints.
    • Fix: Test patterns with preg_match() first:
      if (preg_match('/blacklisted_pattern/', '/path/to/endpoint')) {
          // Will be blocked
      }
      
  5. ETag CachePlugin Quirks:

    • Problem: EtagCachePlugin always revalidates cached responses, which may increase API calls.
    • Fix: Use sparingly for truly immutable resources (e.g., static assets, public APIs).

Debugging Tips

  1. Enable Cache Headers: Add AddHeaderCacheListener to inspect X-Cache headers in responses:

    $cachePlugin = new CachePlugin($cachePool, new SimpleGenerator(), [], [
        new AddHeaderCacheListener()
    ]);
    
  2. Log Cache Hits/Misses: Extend CacheListener to log events:

    $listener = new class implements CacheListener {
        public function onCacheHit(ResponseInterface $response): void
        {
            Log::debug('Cache HIT', ['url' => $response->getEffectiveUri()]);
        }
        public function onCacheMiss(ResponseInterface $response): void
        {
            Log::debug('Cache MISS', ['url' => $response->getEffectiveUri()]);
        }
    };
    
  3. Validate Cache Entries: Manually check cached items for corruption:

    $item = $cachePool->getItem('cache_key');
    if ($item->isHit()) {
        $response = unserialize($item->get());
        // Verify response integrity
    }
    

Extension Points

  1. Custom Cache Policy: Extend CachePolicy to implement business-specific rules (e.g., cache only during off-peak hours):

    class TimeBasedCachePolicy extends CachePolicy {
        public function shouldCache(RequestInterface $request): bool
        {
            return parent::shouldCache($request) &&
                   now()->hour >= 0 && now()->hour < 6; // Cache only at night
        }
    }
    
  2. PSR-6 Cache Adapter: Swap adapters (e.g., Redis → APCu) without changing client code:

    $cachePool = new \Cache\Adapter\ApcuAdapter();
    
  3. Middleware Integration: Combine with Laravel middleware to conditionally enable caching:

    $client = $request->hasHeader('X-Cache-Enabled')
        ? $cachePlugin->attachTo($baseClient)
        : $baseClient;
    

Laravel-Specific Quirks

  1. Service Container Binding: Bind the cached client in AppServiceProvider:

    $this->app->bind(\Http\Client\HttpClient::class, function ($app) {
        $client = new \Http\Client\Common\Plugin\Client\BaseClient();
        $cachePool = $app->make(\Psr\Cache\CacheItemPoolInterface::class);
        return (new CachePlugin($cachePool))->attachTo($client);
    });
    
  2. Queue Job Caching: Avoid caching responses in queue jobs (e.g., HandleWebhook) where idempotency isn’t guaranteed.

  3. **Testing

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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata