Cache::getCacheKey()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 5
c 2
b 0
f 0
dl 0
loc 10
rs 10
cc 2
nc 2
nop 1
1
<?php
2
3
/*
4
 * This file is part of the PHALCON-EXT package.
5
 *
6
 * (c) Jitendra Adhikari <[email protected]>
7
 *     <https://github.com/adhocore>
8
 *
9
 * Licensed under MIT license.
10
 */
11
12
namespace PhalconExt\Http\Middleware;
13
14
use Phalcon\Http\Request;
15
use Phalcon\Http\Response;
16
use PhalconExt\Http\BaseMiddleware;
17
18
/**
19
 * Cache middleware that caches request output for fast performance.
20
 *
21
 * @author  Jitendra Adhikari <[email protected]>
22
 * @license MIT
23
 *
24
 * @link    https://github.com/adhocore/phalcon-ext
25
 */
26
class Cache extends BaseMiddleware
27
{
28
    /** @var string */
29
    protected $cacheKey  = '';
30
31
    protected $configKey = 'httpCache';
32
33
    protected $willCache = false;
34
35
    /**
36
     * Get the recent cache key used.
37
     *
38
     * @return string
39
     */
40
    public function getLastKey(): string
41
    {
42
        return $this->cacheKey;
43
    }
44
45
    /**
46
     * Handle the cache.
47
     *
48
     * @param Request  $request
49
     * @param Response $response
50
     *
51
     * @return bool
52
     */
53
    public function before(Request $request, Response $response): bool
54
    {
55
        if (!$this->isCacheable($request, $response)) {
56
            return true;
57
        }
58
59
        $this->cacheKey = $this->getCacheKey($request);
60
61
        if (!$this->hasCache($this->cacheKey)) {
62
            return $this->willCache = true;
63
        }
64
65
        return $this->serve($response);
66
    }
67
68
    /**
69
     * Check if the output for current request is cachaeble.
70
     *
71
     * @param Request  $request
72
     * @param Response $response
73
     *
74
     * @return bool
75
     */
76
    protected function isCacheable(Request $request, Response $response): bool
77
    {
78
        if (!$request->isGet()) {
79
            return false;
80
        }
81
82
        list($routeName, $url) = $this->getRouteNameUri();
83
84
        $allowedRoutes = \array_fill_keys($this->config['routes'], true);
85
86
        if (!isset($allowedRoutes[$routeName]) && !isset($allowedRoutes[$url])) {
87
            return false;
88
        }
89
90
        $statusCode = $response->getStatusCode();
91
92
        return \in_array($statusCode, [200, 204, 301, null]); // null doesnt indicate failure!
93
    }
94
95
    /**
96
     * Checks if there is cache for key corresponding to current request.
97
     *
98
     * @param string $cacheKey
99
     *
100
     * @return bool
101
     */
102
    protected function hasCache(string $cacheKey): bool
103
    {
104
        return $this->di('redis')->exists($cacheKey);
105
    }
106
107
    /**
108
     * Get cacheKey for current request.
109
     *
110
     * @param Request $request
111
     *
112
     * @return string
113
     */
114
    protected function getCacheKey(Request $request): string
115
    {
116
        if ($this->cacheKey) {
117
            return $this->cacheKey;
118
        }
119
120
        $query = $request->getQuery();
121
        \sort($query);
122
123
        return $this->cacheKey = \md5($request->getUri() . '?' . \http_build_query($query));
124
    }
125
126
    /**
127
     * Output the cached response with correct header.
128
     *
129
     * @param Response $response
130
     *
131
     * @return bool
132
     */
133
    protected function serve(Response $response): bool
134
    {
135
        $cached = \json_decode($this->di('redis')->get($this->cacheKey));
136
137
        foreach ($cached->headers as $name => $value) {
138
            $response->setHeader($name, $value);
139
        }
140
141
        return $this->abort(200, $cached->content);
142
    }
143
144
    /**
145
     * Write the just sent response to cache.
146
     *
147
     * @param Request  $request
148
     * @param Response $response
149
     *
150
     * @return bool
151
     */
152
    public function after(Request $request, Response $response): bool
153
    {
154
        if (!$this->willCache) {
155
            return true;
156
        }
157
158
        $headers = ['X-Cache' => \time(), 'X-Cache-ID' => $this->cacheKey];
159
160
        foreach ($response->getHeaders()->toArray() as $key => $value) {
161
            if (\strpos($key, 'Access-Control-') === false) {
162
                $headers[$key] = $value;
163
            }
164
        }
165
166
        $this->di('redis')->save($this->cacheKey, \json_encode([
167
            'headers' => $headers,
168
            'content' => $this->getContent($response),
169
        ]), $this->config['ttl'] * 60);
170
171
        return true;
172
    }
173
174
    /**
175
     * Get the content string.
176
     *
177
     * @param Response $response
178
     *
179
     * @return string
180
     */
181
    protected function getContent(Response $response): string
182
    {
183
        if (null !== $response->getContent()) {
184
            return $response->getContent();
185
        }
186
187
        $app   = $this->di('application');
188
        $value = $this->isMicro() ? $app->getReturnedValue() : $this->di('dispatcher')->getReturnedValue();
189
190
        return \method_exists($value, 'getContent') ? $value->getContent() : (string) $value;
191
    }
192
}
193