Completed
Pull Request — master (#161)
by Grégoire
01:15
created

SymfonyCache::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 8
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/*
4
 * This file is part of the Sonata Project package.
5
 *
6
 * (c) Thomas Rabaix <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sonata\CacheBundle\Adapter;
13
14
use Sonata\Cache\CacheAdapterInterface;
15
use Sonata\Cache\CacheElementInterface;
16
use Sonata\Cache\Exception\UnsupportedException;
17
use Symfony\Component\Filesystem\Filesystem;
18
use Symfony\Component\HttpFoundation\Response;
19
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
20
use Symfony\Component\Routing\RouterInterface;
21
22
/**
23
 * Handles Symfony cache.
24
 *
25
 * @author Vincent Composieux <[email protected]>
26
 */
27
class SymfonyCache implements CacheAdapterInterface
28
{
29
    /**
30
     * @var RouterInterface
31
     */
32
    protected $router;
33
34
    /**
35
     * @var string
36
     */
37
    protected $cacheDir;
38
39
    /**
40
     * @var string
41
     */
42
    protected $token;
43
44
    /**
45
     * @var string[]
46
     */
47
    protected $types;
48
49
    /**
50
     * @var bool
51
     */
52
    protected $phpCodeCacheEnabled;
53
54
    /**
55
     * @var array
56
     */
57
    protected $servers;
58
59
    /**
60
     * @var array
61
     */
62
    protected $timeouts;
63
64
    /**
65
     * @var Filesystem
66
     */
67
    protected $filesystem;
68
69
    /**
70
     * Constructor.
71
     *
72
     * @param RouterInterface $router              A router instance
73
     * @param Filesystem      $filesystem          A Symfony Filesystem component instance
74
     * @param string          $cacheDir            A Symfony cache directory
75
     * @param string          $token               A token to clear the related cache
76
     * @param bool            $phpCodeCacheEnabled If true, will clear OPcache code cache
77
     * @param array           $types               A cache types array
78
     * @param array           $servers             An array of servers
79
     * @param array           $timeouts            An array of timeout options
80
     */
81
    public function __construct(RouterInterface $router, Filesystem $filesystem, $cacheDir, $token, $phpCodeCacheEnabled, array $types, array $servers, array $timeouts)
82
    {
83
        $this->router = $router;
84
        $this->filesystem = $filesystem;
85
        $this->cacheDir = $cacheDir;
86
        $this->token = $token;
87
        $this->types = $types;
88
        $this->phpCodeCacheEnabled = $phpCodeCacheEnabled;
89
        $this->servers = $servers;
90
        $this->timeouts = $timeouts;
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    public function flushAll(): bool
97
    {
98
        return $this->flush(['all']);
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     *
104
     * @throws \InvalidArgumentException
105
     */
106
    public function flush(array $keys = ['all']): bool
107
    {
108
        $result = true;
109
110
        foreach ($this->servers as $server) {
111
            foreach ($keys as $type) {
112
                $ip = $server['ip'];
113
114
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
115
                    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
116
                } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
117
                    $socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
118
                } else {
119
                    throw new \InvalidArgumentException(sprintf('"%s" is not a valid ip address', $ip));
120
                }
121
122
                // generate the raw http request
123
                $command = sprintf("GET %s HTTP/1.1\r\n", $this->getUrl($type));
124
                $command .= sprintf("Host: %s\r\n", $server['domain']);
125
126
                if ($server['basic']) {
127
                    $command .= sprintf("Authorization: Basic %s\r\n", $server['basic']);
128
                }
129
130
                $command .= "Connection: Close\r\n\r\n";
131
132
                // setup the default timeout (avoid max execution time)
133
                socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $this->timeouts['SND']);
134
                socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $this->timeouts['RCV']);
135
136
                socket_connect($socket, $server['ip'], $server['port']);
137
                socket_write($socket, $command);
138
139
                $content = '';
140
141
                do {
142
                    $buffer = socket_read($socket, 1024);
143
                    $content .= $buffer;
144
                } while (!empty($buffer));
145
146
                if ($result) {
147
                    $result = substr($content, -2) == 'ok';
148
                } else {
149
                    throw new \UnexpectedValueException(sprintf(
150
                        'Server answered with "%s"',
151
                        $content
152
                    ));
153
                }
154
            }
155
        }
156
157
        return $result;
158
    }
159
160
    /**
161
     * Symfony cache action.
162
     *
163
     * @param string $token A Sonata symfony cache token
164
     * @param string $type  A cache type to invalidate (doctrine, translations, twig, ...)
165
     *
166
     * @return Response
167
     *
168
     * @throws AccessDeniedHttpException if token is invalid
169
     * @throws \RuntimeException         if specified type is not in allowed types list
170
     */
171
    public function cacheAction($token, $type)
172
    {
173
        if ($this->token != $token) {
174
            throw new AccessDeniedHttpException('Invalid token');
175
        }
176
177
        if (!in_array($type, $this->types)) {
178
            throw new \RuntimeException(
179
                sprintf('Type "%s" is not defined, allowed types are: "%s"', $type, implode(', ', $this->types))
180
            );
181
        }
182
183
        $path = 'all' == $type ? $this->cacheDir : sprintf('%s/%s', $this->cacheDir, $type);
184
185
        if ($this->filesystem->exists($path)) {
186
            $movedPath = $path.'_old_'.uniqid();
187
188
            $this->filesystem->rename($path, $movedPath);
189
            $this->filesystem->remove($movedPath);
190
191
            $this->clearPHPCodeCache();
192
        }
193
194
        return new Response('ok', 200, [
195
            'Cache-Control' => 'no-cache, must-revalidate',
196
            'Content-Length' => 2, // to prevent chunked transfer encoding
197
        ]);
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203
    public function has(array $keys): bool
204
    {
205
        throw new UnsupportedException('Symfony cache has() method does not exist');
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211
    public function set(array $keys, $data, int $ttl = 84600, array $contextualKeys = []): CacheElementInterface
212
    {
213
        throw new UnsupportedException('Symfony cache set() method does not exist');
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219
    public function get(array $keys): CacheElementInterface
220
    {
221
        throw new UnsupportedException('Symfony cache get() method does not exist');
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227
    public function isContextual(): bool
228
    {
229
        return false;
230
    }
231
232
    /**
233
     * Returns URL with given token used for cache invalidation.
234
     *
235
     * @param string $type
236
     *
237
     * @return string
238
     */
239
    protected function getUrl($type)
240
    {
241
        return $this->router->generate('sonata_cache_symfony', [
242
            'token' => $this->token,
243
            'type' => $type,
244
        ]);
245
    }
246
247
    /**
248
     * Clears code cache with PHP OPcache.
249
     */
250
    protected function clearPHPCodeCache()
251
    {
252
        if (!$this->phpCodeCacheEnabled) {
253
            return;
254
        }
255
256
        if (function_exists('opcache_reset')) {
257
            opcache_reset();
258
        }
259
    }
260
}
261