MemcacheRaw::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 7
nc 2
nop 0
dl 0
loc 11
ccs 8
cts 8
cp 1
crap 2
rs 10
c 1
b 0
f 0
1
<?php
2
/**
3
 * Memcache handler for searching by key substring
4
 *
5
 * @file      MemcachedAdapter.php
6
 *
7
 * PHP version 8.0+
8
 *
9
 * @author    Alexander Yancharuk <alex at itvault dot info>
10
 * @copyright © 2012-2021 Alexander Yancharuk
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 10
	public function __construct()
45
	{
46 10
		$errno = $errstr = null;
47
48
		try {
49 10
			$this->connection = fsockopen(
50 10
				self::$host, self::$port, $errno, $errstr
51 10
			);
52 2
		} catch (Exception $e) {
53 2
			throw new Exception(
54 2
				'Can not connect to Memcache. Host: ' . self::$host . ' Port: ' . self::$port
55 2
			);
56
		}
57
	}
58
59
	/**
60
	 * Save connection params
61
	 *
62
	 * @param string $host
63
	 * @param int    $port
64
	 */
65 10
	public static function setConnectionParams($host, $port)
66
	{
67 10
		self::$host = $host;
68 10
		self::$port = $port;
69
	}
70
71
	/**
72
	 * Close memcache connection
73
	 *
74
	 * @return bool
75
	 */
76 3
	public function disconnect()
77
	{
78 3
		$this->command('quit');
79 3
		return fclose($this->connection);
80
	}
81
82
	/**
83
	 * Run memcache console command
84
	 *
85
	 * @param string $command Internal console memcache command
86
	 *
87
	 * @return bool|string Returns string or false on failure or empty result
88
	 */
89 5
	public function command($command)
90
	{
91 5
		fwrite($this->connection, $command . PHP_EOL);
92
93 5
		return fgets($this->connection);
94
	}
95
96
	/**
97
	 * Method for deletion keys by template
98
	 *
99
	 * ATTENTION: if key contains spaces, for example 'THIS IS KEY::ID:50d98ld',
100
	 * then in cache it will be saved as 'THIS_IS_KEY::ID:50d98ld'. So, template
101
	 * for that key deletion must be look like - 'THIS_IS_KEY'.
102
	 * Deletion can be made by substring, containing in keys. For example
103
	 * '_KEY::ID'.
104
	 *
105
	 * @param string $tpl Substring containing in needed keys
106
	 * @return MemcacheRaw
107
	 */
108 3
	public function delByTemplate($tpl)
109
	{
110 3
		$output = $this->query('stats items');
111 3
		$lines  = explode("\r\n", trim($output));
112
113 3
		$slabs = $this->getSlabs($lines);
114 3
		unset($lines);
115
116 3
		foreach ($slabs as $slab) {
117 2
			$query = "stats cachedump $slab";
118 2
			$output = $this->query($query);
119
120 2
			if (preg_match_all('/ITEM ([^\s]+)/', $output, $match)) {
121 2
				$this->delete($match[1], $tpl);
122
			}
123
		}
124 3
		unset($output, $slabs, $slab, $match);
125
126 3
		return $this;
127
	}
128
129
	/**
130
	 * Run console command with output returning
131
	 *
132
	 * @param string $command Memcache console internal command
133
	 *
134
	 * @return string
135
	 */
136 4
	public function query($command)
137
	{
138 4
		fwrite($this->connection, $command . PHP_EOL);
139
140 4
		$output = '';
141 4
		while (false !== ($str = fgets($this->connection))) {
142 3
			if (1 === preg_match($this->regex, $str)) {
143 1
				break;
144
			}
145
146 3
			$output .= $str;
147
		}
148
149 4
		return $output;
150
	}
151
152
	/**
153
	 * Get slabs array
154
	 *
155
	 * @param $lines
156
	 *
157
	 * @return array
158
	 */
159 3
	protected function getSlabs($lines)
160
	{
161 3
		$regex_items = '/^STAT items:(\d+):number (\d+)$/';
162 3
		$slabs = [];
163
164 3
		foreach ($lines as $line) {
165 3
			if (1 === preg_match($regex_items, $line, $match)) {
166 2
				$slabs[] = "$match[1] $match[2]";
167
			}
168
		}
169
170 3
		return $slabs;
171
	}
172
173
	/**
174
	 * Send delete matched keys from cache
175
	 *
176
	 * @param $match
177
	 * @param $tpl
178
	 */
179 2
	protected function delete($match, $tpl)
180
	{
181 2
		foreach ($match as $key) {
182 2
			if (false === strpos($key, $tpl)) {
183 2
				continue;
184
			}
185
186 1
			$this->command("delete $key");
187
		}
188
	}
189
}
190