Completed
Push — 2.x-dev-kit ( 6f250c )
by
unknown
02:44
created

VarnishCache::flush()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 11
rs 9.4285
cc 2
eloc 6
nc 2
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\CacheElement;
16
use Symfony\Component\HttpFoundation\Request;
17
use Symfony\Component\HttpFoundation\Response;
18
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
19
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
20
use Symfony\Component\Process\Process;
21
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
22
use Symfony\Component\Routing\RouterInterface;
23
24
/**
25
 * http://www.varnish-cache.org/docs/2.1/reference/varnishadm.html
26
 *  echo vcl.use foo | varnishadm -T localhost:999 -S /var/db/secret
27
 *  echo vcl.use foo | ssh vhost varnishadm -T localhost:999 -S /var/db/secret.
28
 *
29
 *  in the config.yml file :
30
 *     echo %s "%s" | varnishadm -T localhost:999 -S /var/db/secret
31
 *     echo %s "%s" | ssh vhost "varnishadm -T localhost:999 -S /var/db/secret {{ COMMAND }} '{{ EXPRESSION }}'"
32
 */
33
class VarnishCache implements CacheAdapterInterface
34
{
35
    /**
36
     * @var string
37
     */
38
    protected $token;
39
40
    /**
41
     * @var array
42
     */
43
    protected $servers;
44
45
    /**
46
     * @var RouterInterface
47
     */
48
    protected $router;
49
50
    /**
51
     * @var string
52
     */
53
    protected $purgeInstruction;
54
55
    /**
56
     * @var ControllerResolverInterface
57
     */
58
    protected $resolver;
59
60
    /**
61
     * Constructor.
62
     *
63
     * @param string                           $token            A token
64
     * @param array                            $servers          An array of servers
65
     * @param RouterInterface                  $router           A router instance
66
     * @param string                           $purgeInstruction The purge instruction (purge in Varnish 2, ban in Varnish 3)
67
     * @param null|ControllerResolverInterface $resolver         A controller resolver instance
68
     */
69
    public function __construct($token, array $servers, RouterInterface $router, $purgeInstruction, ControllerResolverInterface $resolver = null)
70
    {
71
        $this->token = $token;
72
        $this->servers = $servers;
73
        $this->router = $router;
74
        $this->purgeInstruction = $purgeInstruction;
75
        $this->resolver = $resolver;
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81
    public function flushAll()
82
    {
83
        return $this->runCommand(
84
            $this->purgeInstruction == 'ban' ? 'ban.url' : 'purge',
85
            $this->purgeInstruction == 'ban' ? '.*' : 'req.url ~ .*'
86
        );
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92
    public function flush(array $keys = array())
93
    {
94
        $parameters = array();
95
        foreach ($keys as $key => $value) {
96
            $parameters[] = sprintf('obj.http.%s ~ %s', $this->normalize($key), $value);
97
        }
98
99
        $purge = implode(' && ', $parameters);
100
101
        return $this->runCommand($this->purgeInstruction, $purge);
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function has(array $keys)
108
    {
109
        return true;
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115
    public function get(array $keys)
116
    {
117
        if (!isset($keys['controller'])) {
118
            throw new \RuntimeException('Please define a controller key');
119
        }
120
121
        if (!isset($keys['parameters'])) {
122
            throw new \RuntimeException('Please define a parameters key');
123
        }
124
125
        $content = sprintf('<esi:include src="%s"/>', $this->getUrl($keys));
126
127
        return new CacheElement($keys, new Response($content));
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function set(array $keys, $data, $ttl = CacheElement::DAY, array $contextualKeys = array())
134
    {
135
        return new CacheElement($keys, $data, $ttl, $contextualKeys);
136
    }
137
138
    /**
139
     * Cache action.
140
     *
141
     * @param Request $request
142
     *
143
     * @return mixed
144
     */
145
    public function cacheAction(Request $request)
146
    {
147
        $parameters = $request->get('parameters', array());
148
149
        if ($request->get('token') != $this->computeHash($parameters)) {
150
            throw new AccessDeniedHttpException('Invalid token');
151
        }
152
153
        $subRequest = Request::create('', 'get', $parameters, $request->cookies->all(), array(), $request->server->all());
154
155
        $controller = $this->resolver->getController($subRequest);
156
157
        $subRequest->attributes->add(array('_controller' => $parameters['controller']));
158
        $subRequest->attributes->add($parameters['parameters']);
159
160
        $arguments = $this->resolver->getArguments($subRequest, $controller);
0 ignored issues
show
Security Bug introduced by
It seems like $controller defined by $this->resolver->getController($subRequest) on line 155 can also be of type false; however, Symfony\Component\HttpKe...terface::getArguments() does only seem to accept callable, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
161
162
        // call controller
163
        return call_user_func_array($controller, $arguments);
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169
    public function isContextual()
170
    {
171
        return true;
172
    }
173
174
    /**
175
     * @param string $command
176
     * @param string $expression
177
     *
178
     * @return bool
179
     */
180
    protected function runCommand($command, $expression)
181
    {
182
        $return = true;
183
        foreach ($this->servers as $server) {
184
            $command = str_replace(array('{{ COMMAND }}', '{{ EXPRESSION }}'), array($command, $expression), $server);
185
186
            $process = new Process($command);
187
188
            if ($process->run() == 0) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $process->run() of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
189
                continue;
190
            }
191
192
            $return = false;
193
        }
194
195
        return $return;
196
    }
197
198
    /**
199
     * Gets the URL by the given keys.
200
     *
201
     * @param array $keys
202
     *
203
     * @return string
204
     */
205
    protected function getUrl(array $keys)
206
    {
207
        $parameters = array(
208
            'token' => $this->computeHash($keys),
209
            'parameters' => $keys,
210
        );
211
212
        return $this->router->generate('sonata_cache_esi', $parameters, UrlGeneratorInterface::ABSOLUTE_PATH);
213
    }
214
215
    /**
216
     * Computes the given keys.
217
     *
218
     * @param array $keys
219
     *
220
     * @return string
221
     */
222
    protected function computeHash(array $keys)
223
    {
224
        ksort($keys);
225
226
        return hash('sha256', $this->token.serialize($keys));
227
    }
228
229
    /**
230
     * Normalizes the given key.
231
     *
232
     * @param string $key
233
     *
234
     * @return string
235
     */
236
    protected function normalize($key)
237
    {
238
        return sprintf('x-sonata-cache-%s', str_replace(array('_', '\\'), '-', strtolower($key)));
239
    }
240
}
241