Completed
Push — master ( f1ec7e...ff9c59 )
by Alexander
04:52
created

MemcacheRaw::delByTemplate()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 20
ccs 14
cts 14
cp 1
rs 9.4286
cc 3
eloc 12
nc 3
nop 1
crap 3
1
<?php
2
/**
3
 * Memcache handler for searching by key substring
4
 *
5
 * @file      MemcachedAdapter.php
6
 *
7
 * PHP version 5.4+
8
 *
9
 * @author    Alexander Yancharuk <alex at itvault dot info>
10
 * @copyright © 2012-2015 Alexander Yancharuk <alex at itvault at info>
11
 * @date      Птн Ноя 01 17:52:46 2013
12
 * @license   The BSD 3-Clause License
13
 *            <https://tldrlegal.com/license/bsd-3-clause-license-(revised)>
14
 */
15
16
namespace Veles\Cache\Adapters;
17
18
use Exception;
19
20
/**
21
 * Class for key cleanup by prefix
22
 *
23
 * In memcache and memcached drivers not implemented key deletion by
24
 * template, this is the reason why this class have implementation of
25
 * key deletion through socket connection.
26
 * Can be used with Memcache only.
27
 */
28
class MemcacheRaw
29
{
30
	protected static $host;
31
	protected static $port;
32
33
	/**
34
	 * @var resource
35
	 */
36
	protected $connection;
37
	protected $regex = '/^(?:END|DELETED|NOT_FOUND|OK|ERROR|STORED|NOT_STORED|EXISTS|TOUCHED)\s+$/';
38
39
	/**
40
	 * Constructor. Open connection with Memcache server.
41
	 *
42
	 * @throws Exception
43
	 */
44 1
	public function __construct()
45
	{
46
		try {
47 1
			$this->connection = fsockopen(
48 1
				self::$host, self::$port, $errno, $errstr
49 1
			);
50 1
		} catch (Exception $e) {
51 1
			throw new Exception('Can not connect to Memcache. Host: '
52 1
				. self::$host . ' Port: ' . self::$port
53 1
			);
54
		}
55 1
	}
56
57
	/**
58
	 * Save connection params
59
	 *
60
	 * @param string $host
61
	 * @param int    $port
62
	 */
63 1
	public static function setConnectionParams($host, $port)
64
	{
65 1
		self::$host = $host;
66 1
		self::$port = $port;
67 1
	}
68
69
	/**
70
	 * Close memcache connection
71
	 *
72
	 * @return bool
73
	 */
74 1
	public function disconnect()
75
	{
76 1
		$this->command('quit');
77 1
		return fclose($this->connection);
78
	}
79
80
	/**
81
	 * Run memcache console command
82
	 *
83
	 * @param string $command Internal console memcache command
84
	 *
85
	 * @return MemcacheRaw
86
	 */
87 1
	public function command($command)
88
	{
89 1
		fwrite($this->connection, $command . PHP_EOL);
90 1
		fgets($this->connection);
91 1
	}
92
93
	/**
94
	 * Method for deletion keys by template
95
	 *
96
	 * ATTENTION: if key contains spaces, for example 'THIS IS KEY::ID:50d98ld',
97
	 * then in cache it will be saved as 'THIS_IS_KEY::ID:50d98ld'. So, template
98
	 * for that key deletion must be look like - 'THIS_IS_KEY'.
99
	 * Deletion can be made by substring, containing in keys. For example
100
	 * '_KEY::ID'.
101
	 *
102
	 * @param string $tpl Substring containing in needed keys
103
	 * @return MemcacheRaw
104
	 */
105 1
	public function delByTemplate($tpl)
106
	{
107 1
		$output = $this->query('stats items');
108 1
		$lines  = explode("\r\n", trim($output));
109
110 1
		$slabs = $this->getSlabs($lines);
111 1
		unset($lines);
112
113 1
		foreach ($slabs as $slab) {
114 1
			$query = "stats cachedump $slab";
115 1
			$output = $this->query($query);
116
117 1
			if (preg_match_all('/ITEM ([^\s]+)/', $output, $match)) {
118 1
				$this->delete($match[1], $tpl);
119 1
			}
120 1
		}
121 1
		unset($output, $slabs, $slab, $match);
122
123 1
		return $this;
124
	}
125
126
	/**
127
	 * Run console command with output returning
128
	 *
129
	 * @param string $command Memcache console internal command
130
	 *
131
	 * @return string
132
	 */
133 1
	public function query($command)
134
	{
135 1
		fwrite($this->connection, $command . PHP_EOL);
136
137 1
		$output = '';
138 1
		while (false !== ($str = fgets($this->connection))) {
139 1
			if (1 === preg_match($this->regex, $str)) {
140 1
				break;
141
			}
142
143 1
			$output .= $str;
144 1
		}
145
146 1
		return $output;
147
	}
148
149
	/**
150
	 * Get slabs array
151
	 *
152
	 * @param $lines
153
	 *
154
	 * @return array
155
	 */
156 1
	protected function getSlabs($lines)
157
	{
158 1
		$regex_items = '/^STAT items:(\d+):number (\d+)$/';
159 1
		$slabs = [];
160
161 1
		foreach ($lines as $line) {
162 1
			if (1 === preg_match($regex_items, $line, $match)) {
163 1
				$slabs[] = "$match[1] $match[2]";
164 1
			}
165 1
		}
166
167 1
		return $slabs;
168
	}
169
170
	/**
171
	 * Send delete matched keys from cache
172
	 *
173
	 * @param $match
174
	 * @param $tpl
175
	 */
176 1
	protected function delete($match, $tpl)
177
	{
178 1
		foreach ($match as $key) {
179 1
			if (false === strpos($key, $tpl))
180 1
				continue;
181
182 1
			$this->command("delete $key");
183 1
		}
184 1
	}
185
}
186