Completed
Push — 2.1 ( 28b26f...4d9204 )
by Alexander
10:53
created

MemCached::getValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 1
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\caching;
9
10
use Yii;
11
use yii\base\InvalidConfigException;
12
13
/**
14
 * MemCached implements a cache application component based on [memcached](http://pecl.php.net/package/memcached) PECL
15
 * extension.
16
 *
17
 * MemCached can be configured with a list of memcached servers by settings its [[servers]] property.
18
 * By default, MemCached assumes there is a memcached server running on localhost at port 11211.
19
 *
20
 * See [[\Psr\SimpleCache\CacheInterface]] for common cache operations that MemCached supports.
21
 *
22
 * Note, there is no security measure to protected data in memcached.
23
 * All data in memcached can be accessed by any process running in the system.
24
 *
25
 * To use MemCached as the cache application component, configure the application as follows,
26
 *
27
 * ```php
28
 * [
29
 *     'components' => [
30
 *         'cache' => [
31
 *             'class' => \yii\caching\Cache::class,
32
 *             'handler' => [
33
 *                 'class' => \yii\caching\MemCached::class,
34
 *                 'servers' => [
35
 *                     [
36
 *                         'host' => 'server1',
37
 *                         'port' => 11211,
38
 *                         'weight' => 60,
39
 *                     ],
40
 *                     [
41
 *                         'host' => 'server2',
42
 *                         'port' => 11211,
43
 *                         'weight' => 40,
44
 *                     ],
45
 *                 ],
46
 *             ],
47
 *         ],
48
 *     ],
49
 * ]
50
 * ```
51
 *
52
 * In the above, two memcached servers are used: server1 and server2. You can configure more properties of
53
 * each server, such as `persistent`, `weight`, `timeout`. Please see [[MemCacheServer]] for available options.
54
 *
55
 * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).
56
 *
57
 * @property \Memcached $memcached The memcached object used by this cache component.
58
 * This property is read-only.
59
 * @property MemCachedServer[] $servers List of memcached server configurations. Note that the type of this
60
 * property differs in getter and setter. See [[getServers()]] and [[setServers()]] for details.
61
 *
62
 * @author Qiang Xue <[email protected]>
63
 * @since 2.0
64
 */
65
class MemCached extends SimpleCache
66
{
67
    /**
68
     * @var string an ID that identifies a Memcached instance.
69
     * By default the Memcached instances are destroyed at the end of the request. To create an instance that
70
     * persists between requests, you may specify a unique ID for the instance. All instances created with the
71
     * same ID will share the same connection.
72
     * @see http://ca2.php.net/manual/en/memcached.construct.php
73
     */
74
    public $persistentId;
75
    /**
76
     * @var array options for Memcached.
77
     * @see http://ca2.php.net/manual/en/memcached.setoptions.php
78
     */
79
    public $options;
80
    /**
81
     * @var string memcached sasl username.
82
     * @see http://php.net/manual/en/memcached.setsaslauthdata.php
83
     */
84
    public $username;
85
    /**
86
     * @var string memcached sasl password.
87
     * @see http://php.net/manual/en/memcached.setsaslauthdata.php
88
     */
89
    public $password;
90
91
    /**
92
     * @var \Memcached the Memcached instance
93
     */
94
    private $_cache;
95
    /**
96
     * @var array list of memcached server configurations
97
     */
98
    private $_servers = [];
99
100
101
    /**
102
     * Initializes this application component.
103
     * It creates the memcached instance and adds memcached servers.
104
     */
105 16
    public function init()
106
    {
107 16
        parent::init();
108 16
        $this->addServers($this->getMemcached(), $this->getServers());
109 16
    }
110
111
    /**
112
     * Add servers to the server pool of the cache specified
113
     *
114
     * @param \Memcached $cache
115
     * @param MemCachedServer[] $servers
116
     * @throws InvalidConfigException
117
     */
118 16
    protected function addServers($cache, $servers)
119
    {
120 16
        if (empty($servers)) {
121 16
            $servers = [new MemCachedServer([
122 16
                'host' => '127.0.0.1',
123
                'port' => 11211,
124
            ])];
125
        } else {
126
            foreach ($servers as $server) {
127
                if ($server->host === null) {
128
                    throw new InvalidConfigException("The 'host' property must be specified for every memcached server.");
129
                }
130
            }
131
        }
132
133 16
        $existingServers = [];
134 16
        if ($this->persistentId !== null) {
135
            foreach ($cache->getServerList() as $s) {
136
                $existingServers[$s['host'] . ':' . $s['port']] = true;
137
            }
138
        }
139 16
        foreach ($servers as $server) {
140 16
            if (empty($existingServers) || !isset($existingServers[$server->host . ':' . $server->port])) {
141 16
                $cache->addServer($server->host, $server->port, $server->weight);
142
            }
143
        }
144 16
    }
145
146
    /**
147
     * Returns the underlying memcached object.
148
     * @return \Memcached the memcached object used by this cache component.
149
     * @throws InvalidConfigException if memcached extension is not loaded
150
     */
151 16
    public function getMemcached()
152
    {
153 16
        if ($this->_cache === null) {
154 16
            if (!extension_loaded('memcached')) {
155
                throw new InvalidConfigException('MemCached requires PHP memcached extension to be loaded.');
156
            }
157
158 16
            $this->_cache = $this->persistentId !== null ? new \Memcached($this->persistentId) : new \Memcached;
159 16
            if ($this->username !== null || $this->password !== null) {
160
                $this->_cache->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
161
                $this->_cache->setSaslAuthData($this->username, $this->password);
162
            }
163 16
            if (!empty($this->options)) {
164
                $this->_cache->setOptions($this->options);
0 ignored issues
show
Bug introduced by
The method setOptions() does not exist on Memcached. Did you maybe mean setOption()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
165
            }
166
        }
167
168 16
        return $this->_cache;
169
    }
170
171
    /**
172
     * Returns the memcached server configurations.
173
     * @return MemCachedServer[] list of memcached server configurations.
174
     */
175 16
    public function getServers()
176
    {
177 16
        return $this->_servers;
178
    }
179
180
    /**
181
     * @param array $config list of memcached server configurations. Each element must be an array
182
     * with the following keys: host, port, persistent, weight, timeout, retryInterval, status.
183
     * @see http://php.net/manual/en/memcached.addserver.php
184
     */
185
    public function setServers($config)
186
    {
187
        foreach ($config as $c) {
188
            $this->_servers[] = new MemCachedServer($c);
189
        }
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195 13
    protected function getValue($key)
196
    {
197 13
        return $this->_cache->get($key);
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203 3
    protected function getValues($keys)
204
    {
205 3
        return $this->_cache->getMulti($keys);
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211 12
    protected function setValue($key, $value, $ttl)
212
    {
213
        // Use UNIX timestamp since it doesn't have any limitation
214
        // @see http://php.net/manual/en/memcached.expiration.php
215 12
        $expire = $ttl > 0 ? $ttl + time() : 0;
216
217 12
        return $this->_cache->set($key, $value, $expire);
218
    }
219
220
    /**
221
     * {@inheritdoc}
222
     */
223 4
    protected function setValues($values, $ttl)
224
    {
225
        // Use UNIX timestamp since it doesn't have any limitation
226
        // @see http://php.net/manual/en/memcached.expiration.php
227 4
        $expire = $ttl > 0 ? $ttl + time() : 0;
228
229
        // Memcached::setMulti() returns boolean
230
        // @see http://php.net/manual/en/memcached.setmulti.php
231 4
        return $this->_cache->setMulti($values, $expire) ? [] : array_keys($values);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->_cache->se... : array_keys($values); (array) is incompatible with the return type of the parent method yii\caching\SimpleCache::setValues 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...
232
    }
233
234
    /**
235
     * {@inheritdoc}
236
     */
237 1
    protected function deleteValue($key)
238
    {
239 1
        return $this->_cache->delete($key, 0);
240
    }
241
242
    /**
243
     * {@inheritdoc}
244
     */
245 11
    public function clear()
246
    {
247 11
        return $this->_cache->flush();
248
    }
249
}
250