Completed
Push — master ( cd6aa3...db07f0 )
by Jitendra
13s
created

Cache::before()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 2
1
<?php
2
3
namespace PhalconExt\Http\Middleware;
4
5
use Phalcon\Http\Request;
1 ignored issue
show
Bug introduced by
The type Phalcon\Http\Request was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use Phalcon\Http\Response;
1 ignored issue
show
Bug introduced by
The type Phalcon\Http\Response was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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