Completed
Pull Request — master (#16)
by Yuichi
09:45 queued 07:37
created

CacheSubscriber   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 90
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 10
c 1
b 0
f 1
lcom 1
cbo 8
dl 0
loc 90
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A attach() 0 8 1
A getEvents() 0 7 1
A onBefore() 0 13 2
A onComplete() 0 12 2
A cacheMiss() 0 4 1
A addResponseHeaders() 0 12 2
1
<?php
2
3
namespace CybozuHttp\Subscriber;
4
5
use CybozuHttp\Subscriber\Cache\CacheStorage;
6
use GuzzleHttp\Event\HasEmitterInterface;
7
use GuzzleHttp\Event\BeforeEvent;
8
use GuzzleHttp\Event\CompleteEvent;
9
use GuzzleHttp\Event\RequestEvents;
10
use GuzzleHttp\Event\SubscriberInterface;
11
use GuzzleHttp\Message\RequestInterface;
12
use GuzzleHttp\Message\ResponseInterface;
13
14
/**
15
 * @author ochi51 <[email protected]>
16
 */
17
class CacheSubscriber implements SubscriberInterface
18
{
19
    /** @var CacheStorage $cache Object used to cache responses */
20
    protected $storage;
21
22
    /**
23
     * @param CacheStorage $cache    Cache storage
24
     */
25
    public function __construct(CacheStorage $cache)
26
    {
27
        $this->storage = $cache;
28
    }
29
30
    /**
31
     * @param HasEmitterInterface $subject
32
     * @param CacheStorage|null $storage
33
     * @return array
34
     */
35
    public static function attach(HasEmitterInterface $subject, CacheStorage $storage)
36
    {
37
        $emitter = $subject->getEmitter();
38
        $cache = new self($storage);
39
        $emitter->attach($cache);
40
41
        return ['subscriber' => $cache, 'storage' => $storage];
42
    }
43
44
    /**
45
     * @return array
46
     */
47
    public function getEvents()
48
    {
49
        return [
50
            'before'   => ['onBefore', RequestEvents::LATE],
51
            'complete' => ['onComplete', RequestEvents::EARLY]
52
        ];
53
    }
54
55
    /**
56
     * Checks if a request can be cached, and if so, intercepts with a cached
57
     * response is available.
58
     */
59
    public function onBefore(BeforeEvent $event)
60
    {
61
        $request = $event->getRequest();
62
63
        if (!($response = $this->storage->fetch($request))) {
64
            $this->cacheMiss($request);
65
            return;
66
        }
67
68
        $request->getConfig()->set('cache_lookup', 'HIT');
69
        $request->getConfig()->set('cache_hit', true);
70
        $event->intercept($response);
71
    }
72
73
    /**
74
     * Checks if the request and response can be cached, and if so, store it
75
     */
76
    public function onComplete(CompleteEvent $event)
77
    {
78
        $request = $event->getRequest();
79
        $response = $event->getResponse();
80
81
        // Cache the response if it can be cached and isn't already
82
        if ($request->getConfig()->get('cache_lookup') === 'MISS') {
83
            $this->storage->cache($request, $response);
0 ignored issues
show
Bug introduced by
It seems like $response defined by $event->getResponse() on line 79 can be null; however, CybozuHttp\Subscriber\Cache\CacheStorage::cache() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
84
        }
85
86
        $this->addResponseHeaders($request, $response);
0 ignored issues
show
Bug introduced by
It seems like $response defined by $event->getResponse() on line 79 can be null; however, CybozuHttp\Subscriber\Ca...r::addResponseHeaders() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
87
    }
88
89
    private function cacheMiss(RequestInterface $request)
90
    {
91
        $request->getConfig()->set('cache_lookup', 'MISS');
92
    }
93
94
    private function addResponseHeaders(RequestInterface $request, ResponseInterface $response)
95
    {
96
        $params = $request->getConfig();
97
        $lookup = $params['cache_lookup'] . ' from GuzzleCache';
98
        $response->addHeader('X-Cache-Lookup', $lookup);
99
100
        if ($params['cache_hit'] === true) {
101
            $response->addHeader('X-Cache', 'HIT from GuzzleCache');
102
        } else {
103
            $response->addHeader('X-Cache', 'MISS from GuzzleCache');
104
        }
105
    }
106
}