Completed
Push — 2.x-dev-kit ( 3d52ae )
by
unknown
09:33
created

SymfonyCache::cacheAction()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 28
rs 8.439
cc 5
eloc 15
nc 6
nop 2
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\Request;
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
     * Constructor.
61
     *
62
     * @param RouterInterface $router              A router instance
63
     * @param Filesystem      $filesystem          A Symfony Filesystem component instance
64
     * @param string          $cacheDir            A Symfony cache directory
65
     * @param string          $token               A token to clear the related cache
66
     * @param bool            $phpCodeCacheEnabled If true, will clear APC or PHP OPcache code cache
67
     * @param array           $types               A cache types array
68
     * @param array           $servers             An array of servers
69
     */
70
    public function __construct(RouterInterface $router, Filesystem $filesystem, $cacheDir, $token, $phpCodeCacheEnabled, array $types, array $servers)
71
    {
72
        $this->router = $router;
73
        $this->filesystem = $filesystem;
0 ignored issues
show
Bug introduced by
The property filesystem does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
74
        $this->cacheDir = $cacheDir;
75
        $this->token = $token;
76
        $this->types = $types;
0 ignored issues
show
Documentation Bug introduced by
It seems like $types of type array is incompatible with the declared type string of property $types.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
77
        $this->phpCodeCacheEnabled = $phpCodeCacheEnabled;
78
        $this->servers = $servers;
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84
    public function flushAll()
85
    {
86
        return $this->flush(array('all'));
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->flush(array('all')); of type string|boolean adds the type string to the return on line 86 which is incompatible with the return type declared by the interface Sonata\Cache\CacheAdapterInterface::flushAll of type boolean.
Loading history...
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92
    public function flush(array $keys = array('all'))
93
    {
94
        $result = true;
95
96
        foreach ($this->servers as $server) {
97
            foreach ($keys as $type) {
98
                if (count(explode('.', $server['ip']) == 3)) {
99
                    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
100
                } else {
101
                    $socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
102
                }
103
104
                // generate the raw http request
105
                $command = sprintf("GET %s HTTP/1.1\r\n", $this->getUrl($type));
106
                $command .= sprintf("Host: %s\r\n", $server['domain']);
107
108
                if ($server['basic']) {
109
                    $command .= sprintf("Authorization: Basic %s\r\n", $server['basic']);
110
                }
111
112
                $command .= "Connection: Close\r\n\r\n";
113
114
                // setup the default timeout (avoid max execution time)
115
                socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array('sec' => 2, 'usec' => 0));
116
                socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 2, 'usec' => 0));
117
118
                socket_connect($socket, $server['ip'], $server['port']);
119
                socket_write($socket, $command);
120
121
                $content = '';
122
123
                do {
124
                    $buffer = socket_read($socket, 1024);
125
                    $content .= $buffer;
126
                } while (!empty($buffer));
127
128
                if ($result) {
129
                    $result = substr($content, -2) == 'ok';
130
                } else {
131
                    return $content;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $content; (string) is incompatible with the return type declared by the interface Sonata\Cache\CacheAdapterInterface::flush of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
132
                }
133
            }
134
        }
135
136
        return $result;
137
    }
138
139
    /**
140
     * Symfony cache action.
141
     *
142
     * @param string $token A Sonata symfony cache token
143
     * @param string $type  A cache type to invalidate (doctrine, translations, twig, ...)
144
     *
145
     * @return Response
146
     *
147
     * @throws AccessDeniedHttpException if token is invalid
148
     * @throws \RuntimeException         if specified type is not in allowed types list
149
     */
150
    public function cacheAction($token, $type)
151
    {
152
        if ($this->token != $token) {
153
            throw new AccessDeniedHttpException('Invalid token');
154
        }
155
156
        if (!in_array($type, $this->types)) {
157
            throw new \RuntimeException(
158
                sprintf('Type "%s" is not defined, allowed types are: "%s"', $type, implode(', ', $this->types))
159
            );
160
        }
161
162
        $path = 'all' == $type ? $this->cacheDir : sprintf('%s/%s', $this->cacheDir, $type);
163
164
        if ($this->filesystem->exists($path)) {
165
            $movedPath = $path.'_old_'.uniqid();
166
167
            $this->filesystem->rename($path, $movedPath);
168
            $this->filesystem->remove($movedPath);
169
170
            $this->clearPHPCodeCache();
171
        }
172
173
        return new Response('ok', 200, array(
174
            'Cache-Control' => 'no-cache, must-revalidate',
175
            'Content-Length' => 2, // to prevent chunked transfer encoding
176
        ));
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182
    public function has(array $keys)
183
    {
184
        throw new UnsupportedException('Symfony cache has() method does not exists');
185
    }
186
187
    /**
188
     * {@inheritdoc}
189
     */
190
    public function set(array $keys, $data, $ttl = 84600, array $contextualKeys = array())
191
    {
192
        throw new UnsupportedException('Symfony cache set() method does not exists');
193
    }
194
195
    /**
196
     * {@inheritdoc}
197
     */
198
    public function get(array $keys)
199
    {
200
        throw new UnsupportedException('Symfony cache get() method does not exists');
201
    }
202
203
    /**
204
     * {@inheritdoc}
205
     */
206
    public function isContextual()
207
    {
208
        return false;
209
    }
210
211
    /**
212
     * Returns URL with given token used for cache invalidation.
213
     *
214
     * @param string $type
215
     *
216
     * @return string
217
     */
218
    protected function getUrl($type)
219
    {
220
        return $this->router->generate('sonata_cache_symfony', array(
221
            'token' => $this->token,
222
            'type' => $type,
223
        ));
224
    }
225
226
    /**
227
     * Clears code cache with:.
228
     *
229
     * PHP < 5.5.0: APC
230
     * PHP >= 5.5.0: PHP OPcache
231
     */
232
    protected function clearPHPCodeCache()
233
    {
234
        if (!$this->phpCodeCacheEnabled) {
235
            return;
236
        }
237
238
        if (version_compare(PHP_VERSION, '5.5.0', '>=') && function_exists('opcache_reset')) {
239
            opcache_reset();
240
        } elseif (function_exists('apc_fetch')) {
241
            apc_clear_cache();
242
        }
243
    }
244
}
245