Completed
Push — master ( 7a1b44...7a7f31 )
by Nate
05:15 queued 03:30
created

Cache::cacheResponse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 0
cts 14
cp 0
rs 9.0856
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 2
crap 2
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://github.com/flipbox/relay-stash/blob/master/LICENSE
6
 * @link       https://github.com/flipbox/relay-stash
7
 */
8
9
namespace Flipbox\Stash\Middleware;
10
11
use Flipbox\Stash\Exceptions\InvalidCachePoolException;
12
use Flipbox\Http\Stream\Factory as StreamFactory;
13
use Flipbox\Relay\Middleware\AbstractMiddleware;
14
use Psr\Cache\CacheItemPoolInterface;
15
use Psr\Http\Message\RequestInterface;
16
use Psr\Http\Message\ResponseInterface;
17
use Psr\Http\Message\StreamInterface;
18
use Stash\Interfaces\ItemInterface;
19
20
/**
21
 * @author Flipbox Factory <[email protected]>
22
 * @since 1.0.0
23
 */
24
class Cache extends AbstractMiddleware
25
{
26
27
    /**
28
     * @var CacheItemPoolInterface The connection
29
     */
30
    public $pool;
31
32
    /**
33
     * @inheritdoc
34
     */
35
    public function init()
36
    {
37
        // Parent
38
        parent::init();
39
40
        // Ensure we have a valid pool
41
        if (!$this->pool instanceof CacheItemPoolInterface) {
42
            throw new InvalidCachePoolException(
43
                sprintf(
44
                    "The class '%s' requires a cache pool that is an instance of '%s', '%s' given.",
45
                    get_class($this),
46
                    CacheItemPoolInterface::class,
47
                    get_class($this->pool)
48
                )
49
            );
50
        }
51
    }
52
53
    /**
54
     * @inheritdoc
55
     */
56
    public function __invoke(
57
        RequestInterface $request,
58
        ResponseInterface $response,
59
        callable $next = null
60
    ): ResponseInterface {
61
        // Do parent (logging)
62
        parent::__invoke($request, $response);
63
64
        // Create a cache key
65
        $key = $this->getCacheKey($request);
66
67
        /** @var ItemInterface $item */
68
        $item = $this->pool->getItem($key);
69
70
        // If it's cached
71
        if ($item->isHit()) {
72
            return $this->applyCacheToResponseBody($response, $item);
73
        } else {
74
            // Log
75
            $this->info(
76
                "Item not found in Cache. [key: {key}]",
77
                [
78
                    'key' => $key
79
                ]
80
            );
81
        }
82
83
        // Lock item
84
        $item->lock();
85
86
        /** @var ResponseInterface $response */
87
        $response = $next($request, $response);
88
89
        // Only cache successful responses
90
        if ($this->isResponseSuccessful($response)) {
91
            $this->cacheResponse($response, $item);
92
        } else {
93
            // Log
94
            $this->info(
95
                "Did not save to cache because request was unsuccessful.",
96
                [
97
                    'key' => $key,
98
                    'statusCode' => $response->getStatusCode()
99
                ]
100
            );
101
        }
102
103
        return $response;
104
    }
105
106
    /**
107
     * @param ResponseInterface $response
108
     * @param ItemInterface $item
109
     * @return ResponseInterface
110
     */
111
    protected function applyCacheToResponseBody(ResponseInterface $response, ItemInterface $item)
112
    {
113
        // Log
114
        $this->info(
115
            "Item found in Cache. [key: {key}, expires: {expires}]",
116
            [
117
                'key' => $item->getKey(),
118
                'expires' => $item->getExpiration()->getTimestamp()
119
            ]
120
        );
121
122
        // Add response body
123
        return $response->withBody(
124
            StreamFactory::create($item->get())
125
        );
126
    }
127
128
    /**
129
     * @param ResponseInterface $response
130
     * @param ItemInterface $item
131
     */
132
    protected function cacheResponse(ResponseInterface $response, ItemInterface $item)
133
    {
134
        /** @var StreamInterface $body */
135
        $body = $response->getBody();
136
137
        // Set cache contents
138
        $item->set($body->getContents());
139
140
        // Save cache item
141
        $this->pool->save($item);
142
143
        // Rewind stream
144
        $body->rewind();
145
146
        // Log
147
        $this->info(
148
            "Save item to Cache. [key: {key}, expires: {expires}]",
149
            [
150
                'key' => $item->getKey(),
151
                'expires' => $item->getExpiration()->getTimestamp()
152
            ]
153
        );
154
    }
155
156
    /**
157
     * Returns the id used to cache a request.
158
     *
159
     * @param RequestInterface $request
160
     *
161
     * @return string
162
     */
163
    private function getCacheKey(RequestInterface $request): string
164
    {
165
        return $request->getMethod() . md5((string)$request->getUri());
166
    }
167
}
168