iqbalmalik89/guzzle-application-cache-subscriber

Guzzle Plugin resp. Guzzle Subscriber to selectively cache a request/response on application level.

0.7.5 2015-12-06 22:52 UTC

This package is auto-updated.

Last update: 2025-06-19 23:28:41 UTC


README

This repository has been deprecated as of 2019-01-27. That code was written a long time ago and has been unmaintained for several years. Thus, repository will now be archived. If you are interested in taking over ownership, feel free to contact me.

guzzle-application-cache-subscriber

Build Status

Plugin for Guzzle 5 to cache requests/responses on an application level scope. This is different from the CacheSubscriber because it doesn't adhere to HTTP caching standards but leaves it to the developer to decide what to cache and when to request a fresh response. Although I shamelessly used the CacheStorage from said plugin :)

Description

This plugin works as a transparent proxy for requests made to the same URL using the same HTTP verb. I use it frequently when developing API wrappers to avoid making multiple calls to the same API endpoint. This is particularly helpful in cases where the API usage is restricted to a certain amount of requests.

The underlying cache library is Doctrine/Cache and I like to use the FilesystemCache to store responses on disk and have it available on the next test run.

Basic Usage

$cache = new CacheStorage(new ArrayCache());
$sub = new ApplicationCacheSubscriber($cache);
$client = new Client();
$client->getEmitter()->attach($sub);

$num = 5;
$url = "http://www.example.com/";
for ($i = 1; $i <= $num; $i++) {
    echo "Making $i. request:\n";
    $resp = $client->get($url, ["debug" => true]);
    echo "Status code of $i. request: ".$resp->getStatusCode()."\n";
}

Output

Making 1. request:
* Hostname was NOT found in DNS cache
*   Trying 93.184.216.119...
* Connected to www.example.com (93.184.216.119) port 80 (#0)
> GET / HTTP/1.1
Host: www.example.com
User-Agent: Guzzle/5.0.3 curl/7.36.0 PHP/5.5.11

< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: max-age=604800
< Content-Type: text/html
< Date: Thu, 13 Nov 2014 09:54:15 GMT
< Etag: "359670651"
< Expires: Thu, 20 Nov 2014 09:54:15 GMT
< Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
* Server ECS (iad/182A) is not blacklisted
< Server: ECS (iad/182A)
< X-Cache: HIT
< x-ec-custom-error: 1
< Content-Length: 1270
< 
* Connection #0 to host www.example.com left intact
Status code of 1. request: 200
Making 2. request:
Status code of 2. request: 200
Making 3. request:
Status code of 3. request: 200
Making 4. request:
Status code of 4. request: 200
Making 5. request:
Status code of 5. request: 200

Notice how only the first request produced a debug outbut. The remaining requests have been fetched from the cache and were intercepted in the before event.

Examples

See examples folder.

Requirements

  • PHP >= 5.5
  • Guzzle >= 5.3.0
  • Doctrine/Cache >= 1.3.1

Installation

The recommended way to install guzzle-application-cache-subscriber is through Composer.

curl -sS https://getcomposer.org/installer | php

Next, update your project's composer.json file to include GuzzleApplicationCacheSubscriber:

{
    "repositories": [ { "type": "composer", "url": "http://packages.myseosolution.de/"} ],
    "minimum-stability": "dev",
    "require": {
         "paslandau/guzzle-application-cache-subscriber": "dev-master"
    }
    "config": {
        "secure-http": false
    }
}

Caution: You need to explicitly set "secure-http": false in order to access http://packages.myseosolution.de/ as repository. This change is required because composer changed the default setting for secure-http to true at the end of february 2016.

After installing, you need to require Composer's autoloader:

require 'vendor/autoload.php';

General workflow and customization options

The guzzle-application-cache-subscriber uses a closures (canCacheRequest) that is evaluated in the end event to decide wether a request/response can be stored in the cache. If it returns true and the response is not null, it is stored in the cache.

On a subsequent call to the same URL using the same HTTP verb, another closure (mustRequestFresh) is used in the before event to determine if the request can be answered from cache or not. If it returns true and the corresponding response has been cached before, the configuration key has_cached_response (ApplicationCacheSubscriber::CACHED_RESPONSE_KEY) is set to true so that this info might be evaluated later on. On false, the cached response is deleted and Guzzle proceeds to perform the request as usual.

Setting up the validation closures

$cache = new CacheStorage(new ArrayCache());

/**
 * Just some functions / closures for convenience
 */

$getConfigKeyValue = function (AbstractRequestEvent $e, $configKey){
    $request = $e->getRequest();
    return $request->getConfig()->get($configKey);
};

$setConfigKeyValue = function (AbstractRequestEvent $e, $configKey){
    $request = $e->getRequest();
    return $request->getConfig()->set($configKey, true);
};

/**
 * Setup the closures
 */

$mustRequestFreshKey = "requestFresh";
$mustRequestFresh = function(BeforeEvent $event) use ($mustRequestFreshKey, $getConfigKeyValue){
    $val = $getConfigKeyValue($event,$mustRequestFreshKey);
    if($val === null){
        return false;
    }
    if($val){
        echo "Making a fresh request.\n";
        return true;
    }else{
        echo "Trying to serve the response from cache.\n";
        return false;
    }
};

$canCacheRequestKey = "canCacheRequest";
$canCacheRequest = function(EndEvent $event) use ($canCacheRequestKey, $getConfigKeyValue){
    $val = $getConfigKeyValue($event,$canCacheRequestKey);
    if($val === null){
        return true;
    }
    if($val){
        echo "Caching the request/response.\n";
        return true;
    }else{
        echo "Not allowed to cache the request/response.\n";
        return false;
    }
};

$sub = new ApplicationCacheSubscriber($cache, $canCacheRequest, $mustRequestFresh);
$client = new Client();
$client->getEmitter()->attach($sub);

$url = "http://www.example.com/";
$requests = [];

//First request, caching is allowed
$r = $client->createRequest("GET",$url);
$r->getConfig()->add($canCacheRequestKey,true);
$requests[] = $r;
//Second request, get from cache
$r = $client->createRequest("GET",$url);
$r->getConfig()->add($mustRequestFreshKey,false);
$requests[] = $r;
//Third request, force fresh and disallow caching
$r = $client->createRequest("GET",$url);
$r->getConfig()->add($mustRequestFreshKey,true);
$r->getConfig()->add($canCacheRequestKey,false);
$requests[] = $r;
//Fourth request, try to serve from cache and allow caching
$r = $client->createRequest("GET",$url);
$r->getConfig()->add($mustRequestFreshKey,false);
$requests[] = $r;
//Fifth request, get it again from cache
$r = $client->createRequest("GET",$url);
$r->getConfig()->add($mustRequestFreshKey,false);
$requests[] = $r;

foreach ($requests as $i => $request) {
    echo "Request $i\n";
    $resp = $client->send($request);
    if($request->getConfig()->get(ApplicationCacheSubscriber::CACHED_RESPONSE_KEY)) {
        echo "The response came from cache\n\n";
    }else{
        echo "The response came not from cache\n\n";
    }
}

Output

Request 0
Caching the request/response.
The response came not from cache

Request 1
Trying to serve the response from cache.
The response came from cache

Request 2
Making a fresh request.
Not allowed to cache the request/response.
The response came not from cache

Request 3
Trying to serve the response from cache.
The response came not from cache

Request 4
Trying to serve the response from cache.
The response came from cache

Similar plugins

Frequently searched questions

  • How can I cache Guzzle requests/responses?