GlideMiddleware::checkSignature()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
/*
4
 * This file is part of the slince/think-glide
5
 *
6
 * (c) Slince <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Slince\Glide;
13
14
use League\Glide\Server;
15
use League\Glide\ServerFactory;
16
use League\Glide\Signatures\SignatureFactory;
17
use League\Glide\Urls\UrlBuilderFactory;
18
use Symfony\Component\OptionsResolver\OptionsResolver;
19
use think\Container;
20
use think\facade\App;
21
use think\Request;
22
use think\Response;
23
24
class GlideMiddleware
25
{
26
    /**
27
     * @var array
28
     */
29
    protected $options;
30
31
    /**
32
     * @var array
33
     */
34
    protected $query;
35
36
    public function __construct(array $options = [])
37
    {
38
        $resolver = new OptionsResolver();
39
        $resolver->setDefaults([
40
            'baseUrl' => '/images',
41
            'cache' => App::getRuntimePath().'/glide',
42
            'cacheTime' => '+1 day',
43
            'signKey' => false,
44
            'glide' => [],
45
            'onException' => function(\Exception $exception, Request $request, Server $server){
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $server is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
46
                throw $exception;
47
            },
48
        ]);
49
        $resolver->setRequired('source');
50
51
        $this->options = $resolver->resolve($options);
52
53
        //如果启动安全校验,需要注入服务
54
        if ($this->options['signKey']) {
55
            $urlBuilder = UrlBuilderFactory::create($this->options['baseUrl'], $this->options['signKey']);
56
            Container::set('glide.url_builder', $urlBuilder);
0 ignored issues
show
Bug introduced by
The method set() does not seem to exist on object<think\Container>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
57
        }
58
    }
59
60
    public function __invoke(Request $request, $next)
61
    {
62
        $uri = urldecode($request->app() . '/' . $request->path());
63
        parse_str($request->query(), $this->query);
64
65
        if (!preg_match("#^{$this->options['baseUrl']}#", '/'.$uri)) {
66
            return $next($request);
67
        }
68
69
        $server = $this->createGlideServer();
70
        try {
71
            //检查安全签名
72
            $this->checkSignature($uri);
73
            $response = $this->handleRequest($server, $request);
74
        } catch (\Exception $exception) {
75
            $response = call_user_func($this->options['onException'], $exception, $request, $server);
76
        }
77
78
        return $response;
79
    }
80
81
    /**
82
     * @param Server  $server
83
     * @param Request $request
84
     *
85
     * @return Response
86
     *
87
     * @throws \League\Flysystem\FileNotFoundException
88
     * @throws \League\Glide\Filesystem\FileNotFoundException
89
     */
90
    protected function handleRequest(Server $server, Request $request)
91
    {
92
        //检查是否重新更新了
93
        $modifiedTime = null;
94
        if ($this->options['cacheTime']) {
95
            $modifiedTime = $server->getSource()
96
                ->getTimestamp($server->getSourcePath($request->path()));
97
98
            $response = $this->applyModified($modifiedTime, $request);
0 ignored issues
show
Documentation introduced by
$modifiedTime is of type string|false, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
99
            if (false !== $response) {
100
                return $response;
101
            }
102
        }
103
104
        //如果已经更新了重新从缓存拉取图像
105
        if (null === $server->getResponseFactory()) {
106
            $server->setResponseFactory(new ResponseFactory());
107
        }
108
        $response = $server->getImageResponse($request->path(), $this->query);
109
110
        return $this->applyCacheHeaders($response, $modifiedTime);
111
    }
112
113
    protected function applyCacheHeaders(Response $response, $modifiedTime)
114
    {
115
        $expire = strtotime($this->options['cacheTime']);
116
        $maxAge = $expire - time();
117
118
        return $response
119
            ->header([
120
                'Cache-Control' => 'public,max-age='.$maxAge,
121
                'Date' => gmdate('D, j M Y G:i:s \G\M\T', time()),
122
                'Last-Modified' => gmdate('D, j M Y G:i:s \G\M\T', (int) $modifiedTime),
123
                'Expires' => gmdate('D, j M Y G:i:s \G\M\T', $expire)
124
            ]);
125
    }
126
127
    /**
128
     * @param int     $modifiedTime
129
     * @param Request $request
130
     *
131
     * @return false|Response
132
     */
133
    protected function applyModified($modifiedTime, Request $request)
134
    {
135
        //如果没有修改直接返回
136
        if ($this->isNotModified($request, $modifiedTime)) {
137
            $response = new Response('', 304);
138
139
            return $this->applyCacheHeaders($response, $modifiedTime);
140
        }
141
142
        return false;
143
    }
144
145
    /**
146
     * @param Request $request
147
     * @param $modifiedTime
148
     *
149
     * @return bool
150
     */
151
    protected function isNotModified(Request $request, $modifiedTime)
152
    {
153
        $modifiedSince = $request->header('If-Modified-Since');
154
155
        if (!$modifiedSince) {
156
            return false;
157
        }
158
159
        return strtotime($modifiedSince) === (int) $modifiedTime;
160
    }
161
162
    /**
163
     * @param string $uri
164
     *
165
     * @throws \League\Glide\Signatures\SignatureException
166
     */
167
    protected function checkSignature($uri)
168
    {
169
        if (!$this->options['signKey']) {
170
            return;
171
        }
172
        SignatureFactory::create($this->options['signKey'])->validateRequest(
173
            $uri,
174
            $this->query
175
        );
176
    }
177
178
    /**
179
     * @return \League\Glide\Server
180
     */
181
    protected function createGlideServer()
182
    {
183
        return ServerFactory::create(array_merge([
184
            'source' => $this->options['source'],
185
            'cache' => $this->options['cache'],
186
            'base_url' => $this->options['baseUrl'],
187
        ], $this->options['glide']));
188
    }
189
190
    /**
191
     * 创建 middleware 闭包.
192
     *
193
     * @param array $options
194
     *
195
     * @return \Closure
196
     */
197
    public static function factory($options)
198
    {
199
        $middleware = new self($options);
200
201
        return function(Request $request, $next) use ($middleware){
202
            return $middleware($request, $next);
203
        };
204
    }
205
}
206