Completed
Pull Request — master (#122)
by Grégoire
01:49
created

SymfonyCache::get()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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\Exception\UnsupportedException;
16
use Symfony\Component\Filesystem\Filesystem;
17
use Symfony\Component\HttpFoundation\Response;
18
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
19
use Symfony\Component\Routing\RouterInterface;
20
21
/**
22
 * Handles Symfony cache.
23
 *
24
 * @author Vincent Composieux <[email protected]>
25
 */
26
class SymfonyCache implements CacheAdapterInterface
27
{
28
    /**
29
     * @var RouterInterface
30
     */
31
    protected $router;
32
33
    /**
34
     * @var string
35
     */
36
    protected $cacheDir;
37
38
    /**
39
     * @var string
40
     */
41
    protected $token;
42
43
    /**
44
     * @var string
45
     */
46
    protected $types;
47
48
    /**
49
     * @var bool
50
     */
51
    protected $phpCodeCacheEnabled;
52
53
    /**
54
     * @var array
55
     */
56
    protected $servers;
57
58
    /**
59
     * @var array
60
     */
61
    protected $timeouts;
62
63
    /**
64
     * Constructor.
65
     *
66
     * @param RouterInterface $router              A router instance
67
     * @param Filesystem      $filesystem          A Symfony Filesystem component instance
68
     * @param string          $cacheDir            A Symfony cache directory
69
     * @param string          $token               A token to clear the related cache
70
     * @param bool            $phpCodeCacheEnabled If true, will clear APC or PHP OPcache code cache
71
     * @param array           $types               A cache types array
72
     * @param array           $servers             An array of servers
73
     * @param array           $timeouts            An array of timeout options
74
     */
75
    public function __construct(RouterInterface $router, Filesystem $filesystem, $cacheDir, $token, $phpCodeCacheEnabled, array $types, array $servers, array $timeouts))
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected ')', expecting ';' or '{'
Loading history...
76
    {
77
        $this->router = $router;
78
        $this->filesystem = $filesystem;
79
        $this->cacheDir = $cacheDir;
80
        $this->token = $token;
81
        $this->types = $types;
82
        $this->phpCodeCacheEnabled = $phpCodeCacheEnabled;
83
        $this->servers = $servers;
84
        $this->timeouts = $timeouts;
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90
    public function flushAll()
91
    {
92
        return $this->flush(array('all'));
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     *
98
     * @throws \InvalidArgumentException
99
     */
100
    public function flush(array $keys = array('all'))
101
    {
102
        $result = true;
103
104
        foreach ($this->servers as $server) {
105
            foreach ($keys as $type) {
106
                $ip = $server['ip'];
107
108
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
109
                    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
110
                } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
111
                    $socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
112
                } else {
113
                    throw new \InvalidArgumentException(sprintf('"%s" is not a valid ip address', $ip));
114
                }
115
116
                // generate the raw http request
117
                $command = sprintf("GET %s HTTP/1.1\r\n", $this->getUrl($type));
118
                $command .= sprintf("Host: %s\r\n", $server['domain']);
119
120
                if ($server['basic']) {
121
                    $command .= sprintf("Authorization: Basic %s\r\n", $server['basic']);
122
                }
123
124
                $command .= "Connection: Close\r\n\r\n";
125
126
                // setup the default timeout (avoid max execution time)
127
                socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $this->timeouts['SND']);
128
                socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $this->timeouts['RCV']);
129
130
                socket_connect($socket, $server['ip'], $server['port']);
131
                socket_write($socket, $command);
132
133
                $content = '';
134
135
                do {
136
                    $buffer = socket_read($socket, 1024);
137
                    $content .= $buffer;
138
                } while (!empty($buffer));
139
140
                if ($result) {
141
                    $result = substr($content, -2) == 'ok';
142
                } else {
143
                    return $content;
144
                }
145
            }
146
        }
147
148
        return $result;
149
    }
150
151
    /**
152
     * Symfony cache action.
153
     *
154
     * @param string $token A Sonata symfony cache token
155
     * @param string $type  A cache type to invalidate (doctrine, translations, twig, ...)
156
     *
157
     * @return Response
158
     *
159
     * @throws AccessDeniedHttpException if token is invalid
160
     * @throws \RuntimeException         if specified type is not in allowed types list
161
     */
162
    public function cacheAction($token, $type)
163
    {
164
        if ($this->token != $token) {
165
            throw new AccessDeniedHttpException('Invalid token');
166
        }
167
168
        if (!in_array($type, $this->types)) {
169
            throw new \RuntimeException(
170
                sprintf('Type "%s" is not defined, allowed types are: "%s"', $type, implode(', ', $this->types))
171
            );
172
        }
173
174
        $path = 'all' == $type ? $this->cacheDir : sprintf('%s/%s', $this->cacheDir, $type);
175
176
        if ($this->filesystem->exists($path)) {
177
            $movedPath = $path.'_old_'.uniqid();
178
179
            $this->filesystem->rename($path, $movedPath);
180
            $this->filesystem->remove($movedPath);
181
182
            $this->clearPHPCodeCache();
183
        }
184
185
        return new Response('ok', 200, array(
186
            'Cache-Control' => 'no-cache, must-revalidate',
187
            'Content-Length' => 2, // to prevent chunked transfer encoding
188
        ));
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194
    public function has(array $keys)
195
    {
196
        throw new UnsupportedException('Symfony cache has() method does not exists');
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202
    public function set(array $keys, $data, $ttl = 84600, array $contextualKeys = array())
203
    {
204
        throw new UnsupportedException('Symfony cache set() method does not exists');
205
    }
206
207
    /**
208
     * {@inheritdoc}
209
     */
210
    public function get(array $keys)
211
    {
212
        throw new UnsupportedException('Symfony cache get() method does not exists');
213
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218
    public function isContextual()
219
    {
220
        return false;
221
    }
222
223
    /**
224
     * Returns URL with given token used for cache invalidation.
225
     *
226
     * @param string $type
227
     *
228
     * @return string
229
     */
230
    protected function getUrl($type)
231
    {
232
        return $this->router->generate('sonata_cache_symfony', array(
233
            'token' => $this->token,
234
            'type' => $type,
235
        ));
236
    }
237
238
    /**
239
     * Clears code cache with:.
240
     *
241
     * PHP < 5.5.0: APC
242
     * PHP >= 5.5.0: PHP OPcache
243
     */
244
    protected function clearPHPCodeCache()
245
    {
246
        if (!$this->phpCodeCacheEnabled) {
247
            return;
248
        }
249
250
        if (version_compare(PHP_VERSION, '5.5.0', '>=') && function_exists('opcache_reset')) {
251
            opcache_reset();
252
        } elseif (function_exists('apc_fetch')) {
253
            apc_clear_cache();
254
        }
255
    }
256
}
257