Complex classes like MemCache often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use MemCache, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
66 | class MemCache extends Cache |
||
67 | { |
||
68 | /** |
||
69 | * @var bool whether to use memcached or memcache as the underlying caching extension. |
||
70 | * If true, [memcached](http://pecl.php.net/package/memcached) will be used. |
||
71 | * If false, [memcache](http://pecl.php.net/package/memcache) will be used. |
||
72 | * Defaults to false. |
||
73 | */ |
||
74 | public $useMemcached = false; |
||
75 | /** |
||
76 | * @var string an ID that identifies a Memcached instance. This property is used only when [[useMemcached]] is true. |
||
77 | * By default the Memcached instances are destroyed at the end of the request. To create an instance that |
||
78 | * persists between requests, you may specify a unique ID for the instance. All instances created with the |
||
79 | * same ID will share the same connection. |
||
80 | * @see http://ca2.php.net/manual/en/memcached.construct.php |
||
81 | */ |
||
82 | public $persistentId; |
||
83 | /** |
||
84 | * @var array options for Memcached. This property is used only when [[useMemcached]] is true. |
||
85 | * @see http://ca2.php.net/manual/en/memcached.setoptions.php |
||
86 | */ |
||
87 | public $options; |
||
88 | /** |
||
89 | * @var string memcached sasl username. This property is used only when [[useMemcached]] is true. |
||
90 | * @see http://php.net/manual/en/memcached.setsaslauthdata.php |
||
91 | */ |
||
92 | public $username; |
||
93 | /** |
||
94 | * @var string memcached sasl password. This property is used only when [[useMemcached]] is true. |
||
95 | * @see http://php.net/manual/en/memcached.setsaslauthdata.php |
||
96 | */ |
||
97 | public $password; |
||
98 | |||
99 | /** |
||
100 | * @var \Memcache|\Memcached the Memcache instance |
||
101 | */ |
||
102 | private $_cache; |
||
103 | /** |
||
104 | * @var array list of memcache server configurations |
||
105 | */ |
||
106 | private $_servers = []; |
||
107 | |||
108 | |||
109 | /** |
||
110 | * Initializes this application component. |
||
111 | * It creates the memcache instance and adds memcache servers. |
||
112 | */ |
||
113 | 16 | public function init() |
|
118 | |||
119 | /** |
||
120 | * Add servers to the server pool of the cache specified |
||
121 | * |
||
122 | * @param \Memcache|\Memcached $cache |
||
123 | * @param MemCacheServer[] $servers |
||
124 | * @throws InvalidConfigException |
||
125 | */ |
||
126 | 16 | protected function addServers($cache, $servers) |
|
127 | { |
||
128 | 16 | if (empty($servers)) { |
|
129 | 16 | $servers = [new MemCacheServer([ |
|
130 | 16 | 'host' => '127.0.0.1', |
|
131 | 'port' => 11211, |
||
132 | ])]; |
||
133 | } else { |
||
134 | foreach ($servers as $server) { |
||
135 | if ($server->host === null) { |
||
136 | throw new InvalidConfigException("The 'host' property must be specified for every memcache server."); |
||
137 | } |
||
138 | } |
||
139 | } |
||
140 | 16 | if ($this->useMemcached) { |
|
141 | 16 | $this->addMemcachedServers($cache, $servers); |
|
|
|||
142 | } else { |
||
143 | $this->addMemcacheServers($cache, $servers); |
||
144 | } |
||
145 | 16 | } |
|
146 | |||
147 | /** |
||
148 | * Add servers to the server pool of the cache specified |
||
149 | * Used for memcached PECL extension. |
||
150 | * |
||
151 | * @param \Memcached $cache |
||
152 | * @param MemCacheServer[] $servers |
||
153 | */ |
||
154 | 16 | protected function addMemcachedServers($cache, $servers) |
|
155 | { |
||
156 | 16 | $existingServers = []; |
|
157 | 16 | if ($this->persistentId !== null) { |
|
158 | foreach ($cache->getServerList() as $s) { |
||
159 | $existingServers[$s['host'] . ':' . $s['port']] = true; |
||
160 | } |
||
161 | } |
||
162 | 16 | foreach ($servers as $server) { |
|
163 | 16 | if (empty($existingServers) || !isset($existingServers[$server->host . ':' . $server->port])) { |
|
164 | 16 | $cache->addServer($server->host, $server->port, $server->weight); |
|
165 | } |
||
166 | } |
||
167 | 16 | } |
|
168 | |||
169 | /** |
||
170 | * Add servers to the server pool of the cache specified |
||
171 | * Used for memcache PECL extension. |
||
172 | * |
||
173 | * @param \Memcache $cache |
||
174 | * @param MemCacheServer[] $servers |
||
175 | */ |
||
176 | protected function addMemcacheServers($cache, $servers) |
||
177 | { |
||
178 | $class = new \ReflectionClass($cache); |
||
179 | $paramCount = $class->getMethod('addServer')->getNumberOfParameters(); |
||
180 | foreach ($servers as $server) { |
||
181 | // $timeout is used for memcache versions that do not have $timeoutms parameter |
||
182 | $timeout = (int) ($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0); |
||
183 | if ($paramCount === 9) { |
||
184 | $cache->addserver( |
||
185 | $server->host, |
||
186 | $server->port, |
||
187 | $server->persistent, |
||
188 | $server->weight, |
||
189 | $timeout, |
||
190 | $server->retryInterval, |
||
191 | $server->status, |
||
192 | $server->failureCallback, |
||
193 | $server->timeout |
||
194 | ); |
||
195 | } else { |
||
196 | $cache->addserver( |
||
197 | $server->host, |
||
198 | $server->port, |
||
199 | $server->persistent, |
||
200 | $server->weight, |
||
201 | $timeout, |
||
202 | $server->retryInterval, |
||
203 | $server->status, |
||
204 | $server->failureCallback |
||
205 | ); |
||
206 | } |
||
207 | } |
||
208 | } |
||
209 | |||
210 | /** |
||
211 | * Returns the underlying memcache (or memcached) object. |
||
212 | * @return \Memcache|\Memcached the memcache (or memcached) object used by this cache component. |
||
213 | * @throws InvalidConfigException if memcache or memcached extension is not loaded |
||
214 | */ |
||
215 | 16 | public function getMemcache() |
|
216 | { |
||
217 | 16 | if ($this->_cache === null) { |
|
218 | 16 | $extension = $this->useMemcached ? 'memcached' : 'memcache'; |
|
219 | 16 | if (!extension_loaded($extension)) { |
|
220 | throw new InvalidConfigException("MemCache requires PHP $extension extension to be loaded."); |
||
221 | } |
||
222 | |||
223 | 16 | if ($this->useMemcached) { |
|
224 | 16 | $this->_cache = $this->persistentId !== null ? new \Memcached($this->persistentId) : new \Memcached; |
|
225 | 16 | if ($this->username !== null || $this->password !== null) { |
|
226 | $this->_cache->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); |
||
227 | $this->_cache->setSaslAuthData($this->username, $this->password); |
||
228 | } |
||
229 | 16 | if (!empty($this->options)) { |
|
230 | $this->_cache->setOptions($this->options); |
||
231 | } |
||
232 | } else { |
||
233 | $this->_cache = new \Memcache; |
||
234 | } |
||
235 | } |
||
236 | |||
237 | 16 | return $this->_cache; |
|
238 | } |
||
239 | |||
240 | /** |
||
241 | * Returns the memcache or memcached server configurations. |
||
242 | * @return MemCacheServer[] list of memcache server configurations. |
||
243 | */ |
||
244 | 16 | public function getServers() |
|
248 | |||
249 | /** |
||
250 | * @param array $config list of memcache or memcached server configurations. Each element must be an array |
||
251 | * with the following keys: host, port, persistent, weight, timeout, retryInterval, status. |
||
252 | * @see http://php.net/manual/en/memcache.addserver.php |
||
253 | * @see http://php.net/manual/en/memcached.addserver.php |
||
254 | */ |
||
255 | public function setServers($config) |
||
256 | { |
||
257 | foreach ($config as $c) { |
||
258 | $this->_servers[] = new MemCacheServer($c); |
||
259 | } |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * Retrieves a value from cache with a specified key. |
||
264 | * This is the implementation of the method declared in the parent class. |
||
265 | * @param string $key a unique key identifying the cached value |
||
266 | * @return mixed|false the value stored in cache, false if the value is not in the cache or expired. |
||
267 | */ |
||
268 | 13 | protected function getValue($key) |
|
272 | |||
273 | /** |
||
274 | * Retrieves multiple values from cache with the specified keys. |
||
275 | * @param array $keys a list of keys identifying the cached values |
||
276 | * @return array a list of cached values indexed by the keys |
||
277 | */ |
||
278 | 2 | protected function getValues($keys) |
|
282 | |||
283 | /** |
||
284 | * Stores a value identified by a key in cache. |
||
285 | * This is the implementation of the method declared in the parent class. |
||
286 | * |
||
287 | * @param string $key the key identifying the value to be cached |
||
288 | * @param mixed $value the value to be cached. |
||
289 | * @see [Memcache::set()](http://php.net/manual/en/memcache.set.php) |
||
290 | * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire. |
||
291 | * @return bool true if the value is successfully stored into cache, false otherwise |
||
292 | */ |
||
293 | 12 | protected function setValue($key, $value, $duration) |
|
302 | |||
303 | /** |
||
304 | * Stores multiple key-value pairs in cache. |
||
305 | * @param array $data array where key corresponds to cache key while value is the value stored |
||
306 | * @param int $duration the number of seconds in which the cached values will expire. 0 means never expire. |
||
307 | * @return array array of failed keys. |
||
308 | */ |
||
309 | 3 | protected function setValues($data, $duration) |
|
310 | { |
||
311 | 3 | if ($this->useMemcached) { |
|
312 | // Use UNIX timestamp since it doesn't have any limitation |
||
313 | // @see http://php.net/manual/en/memcache.set.php |
||
314 | // @see http://php.net/manual/en/memcached.expiration.php |
||
315 | 3 | $expire = $duration > 0 ? $duration + time() : 0; |
|
316 | |||
317 | // Memcached::setMulti() returns boolean |
||
318 | // @see http://php.net/manual/en/memcached.setmulti.php |
||
319 | 3 | return $this->_cache->setMulti($data, $expire) ? [] : array_keys($data); |
|
320 | } else { |
||
321 | return parent::setValues($data, $duration); |
||
322 | } |
||
323 | } |
||
324 | |||
325 | /** |
||
326 | * Stores a value identified by a key into cache if the cache does not contain this key. |
||
327 | * This is the implementation of the method declared in the parent class. |
||
328 | * |
||
329 | * @param string $key the key identifying the value to be cached |
||
330 | * @param mixed $value the value to be cached |
||
331 | * @see [Memcache::set()](http://php.net/manual/en/memcache.set.php) |
||
332 | * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire. |
||
333 | * @return bool true if the value is successfully stored into cache, false otherwise |
||
334 | */ |
||
335 | 2 | protected function addValue($key, $value, $duration) |
|
344 | |||
345 | /** |
||
346 | * Deletes a value with the specified key from cache |
||
347 | * This is the implementation of the method declared in the parent class. |
||
348 | * @param string $key the key of the value to be deleted |
||
349 | * @return bool if no error happens during deletion |
||
350 | */ |
||
351 | 1 | protected function deleteValue($key) |
|
355 | |||
356 | /** |
||
357 | * Deletes all values from cache. |
||
358 | * This is the implementation of the method declared in the parent class. |
||
359 | * @return bool whether the flush operation was successful. |
||
360 | */ |
||
361 | 11 | protected function flushValues() |
|
365 | } |
||
366 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.