2024.8.23
Loading...
Searching...
No Matches
class.bin.memcached.php
Go to the documentation of this file.
1<?php
2/*
3 * Copyright (c) 2021-2024 Bearsampp
4 * License: GNU General Public License version 3 or later; see LICENSE.txt
5 * Author: Bear
6 * Website: https://bearsampp.com
7 * Github: https://github.com/Bearsampp
8 */
9
10/**
11 * Class BinMemcached
12 *
13 * This class represents the Memcached service module in the Bearsampp application.
14 * It handles the configuration, initialization, and management of the Memcached service.
15 */
16class BinMemcached extends Module
17{
18 const SERVICE_NAME = 'bearsamppmemcached';
19 const SERVICE_PARAMS = '-m %d -p %d -U 0 -vv';
20
21 const ROOT_CFG_ENABLE = 'memcachedEnable';
22 const ROOT_CFG_VERSION = 'memcachedVersion';
23
24 const LOCAL_CFG_EXE = 'memcachedExe';
25 const LOCAL_CFG_MEMORY = 'memcachedMemory';
26 const LOCAL_CFG_PORT = 'memcachedPort';
27
28 private $service;
29 private $log;
30
31 private $exe;
32 private $memory;
33 private $port;
34
35 /**
36 * Constructs a BinMemcached object and initializes the Memcached service.
37 *
38 * @param string $id The ID of the module.
39 * @param string $type The type of the module.
40 */
41 public function __construct($id, $type) {
42 Util::logInitClass($this);
43 $this->reload($id, $type);
44 }
45
46 /**
47 * Reloads the configuration and settings for the Memcached service.
48 *
49 * @param string|null $id The ID of the module. If null, the current ID is used.
50 * @param string|null $type The type of the module. If null, the current type is used.
51 */
52 public function reload($id = null, $type = null) {
55
56 $this->name = $bearsamppLang->getValue(Lang::MEMCACHED);
57 $this->version = $bearsamppConfig->getRaw(self::ROOT_CFG_VERSION);
58 parent::reload($id, $type);
59
60 $this->enable = $this->enable && $bearsamppConfig->getRaw(self::ROOT_CFG_ENABLE);
61 $this->service = new Win32Service(self::SERVICE_NAME);
62 $this->log = $bearsamppRoot->getLogsPath() . '/memcached.log';
63
64 if ($this->bearsamppConfRaw !== false) {
65 $this->exe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_EXE];
66 $this->memory = intval($this->bearsamppConfRaw[self::LOCAL_CFG_MEMORY]);
67 $this->port = intval($this->bearsamppConfRaw[self::LOCAL_CFG_PORT]);
68 }
69
70 if (!$this->enable) {
71 Util::logInfo($this->name . ' is not enabled!');
72 return;
73 }
74 if (!is_dir($this->currentPath)) {
75 Util::logError(sprintf($bearsamppLang->getValue(Lang::ERROR_FILE_NOT_FOUND), $this->name . ' ' . $this->version, $this->currentPath));
76 return;
77 }
78 if (!is_dir($this->symlinkPath)) {
79 Util::logError(sprintf($bearsamppLang->getValue(Lang::ERROR_FILE_NOT_FOUND), $this->name . ' ' . $this->version, $this->symlinkPath));
80 return;
81 }
82 if (!is_file($this->bearsamppConf)) {
83 Util::logError(sprintf($bearsamppLang->getValue(Lang::ERROR_CONF_NOT_FOUND), $this->name . ' ' . $this->version, $this->bearsamppConf));
84 return;
85 }
86 if (!is_file($this->exe)) {
87 Util::logError(sprintf($bearsamppLang->getValue(Lang::ERROR_EXE_NOT_FOUND), $this->name . ' ' . $this->version, $this->exe));
88 return;
89 }
90 if (empty($this->memory)) {
91 Util::logError(sprintf($bearsamppLang->getValue(Lang::ERROR_INVALID_PARAMETER), self::LOCAL_CFG_MEMORY, $this->memory));
92 return;
93 }
94 if (empty($this->port)) {
95 Util::logError(sprintf($bearsamppLang->getValue(Lang::ERROR_INVALID_PARAMETER), self::LOCAL_CFG_PORT, $this->port));
96 return;
97 }
98
99 $nssm = new Nssm(self::SERVICE_NAME);
100 $nssm->setDisplayName(APP_TITLE . ' ' . $this->getName());
101 $nssm->setBinPath($this->exe);
102 $nssm->setParams(sprintf(self::SERVICE_PARAMS, $this->memory, $this->port));
103 $nssm->setStart(Nssm::SERVICE_DEMAND_START);
104 $nssm->setStdout($bearsamppRoot->getLogsPath() . '/memcached.out.log');
105 $nssm->setStderr($bearsamppRoot->getLogsPath() . '/memcached.err.log');
106
107 $this->service->setNssm($nssm);
108 }
109
110 /**
111 * Replaces multiple key-value pairs in the configuration file.
112 *
113 * @param array $params An associative array of key-value pairs to replace.
114 */
115 protected function replaceAll($params) {
116 $content = file_get_contents($this->bearsamppConf);
117
118 foreach ($params as $key => $value) {
119 $content = preg_replace('|' . $key . ' = .*|', $key . ' = ' . '"' . $value.'"', $content);
120 $this->bearsamppConfRaw[$key] = $value;
121 switch ($key) {
123 $this->memory = intval($value);
124 break;
126 $this->port = intval($value);
127 break;
128 }
129 }
130
131 file_put_contents($this->bearsamppConf, $content);
132 }
133
134 /**
135 * Rebuilds the configuration for the Memcached service in the Windows Registry.
136 *
137 * @return bool True if the configuration was successfully rebuilt, false otherwise.
138 */
139 public function rebuildConf() {
140 global $bearsamppRegistry;
141
142 $exists = $bearsamppRegistry->exists(
144 'SYSTEM\CurrentControlSet\Services\\' . self::SERVICE_NAME . '\Parameters',
146 );
147 if ($exists) {
148 return $bearsamppRegistry->setExpandStringValue(
150 'SYSTEM\CurrentControlSet\Services\\' . self::SERVICE_NAME . '\Parameters',
152 sprintf(self::SERVICE_PARAMS, $this->memory, $this->port)
153 );
154 }
155
156 return false;
157 }
158
159 /**
160 * Changes the port for the Memcached service.
161 *
162 * @param int $port The new port number.
163 * @param bool $checkUsed Whether to check if the port is already in use.
164 * @param mixed $wbProgressBar The progress bar object for UI updates.
165 * @return bool|int True if the port was successfully changed, false if the port is invalid, or the process using the port.
166 */
167 public function changePort($port, $checkUsed = false, $wbProgressBar = null) {
168 global $bearsamppWinbinder;
169
170 if (!Util::isValidPort($port)) {
171 Util::logError($this->getName() . ' port not valid: ' . $port);
172 return false;
173 }
174
175 $port = intval($port);
176 $bearsamppWinbinder->incrProgressBar($wbProgressBar);
177
178 $isPortInUse = Util::isPortInUse($port);
179 if (!$checkUsed || $isPortInUse === false) {
180 // bearsampp.conf
181 $this->setPort($port);
182 $bearsamppWinbinder->incrProgressBar($wbProgressBar);
183
184 // conf
185 $this->update();
186 $bearsamppWinbinder->incrProgressBar($wbProgressBar);
187
188 return true;
189 }
190
191 Util::logDebug($this->getName() . ' port in used: ' . $port . ' - ' . $isPortInUse);
192 return $isPortInUse;
193 }
194
195 /**
196 * Checks if the specified port is in use by the Memcached service.
197 *
198 * @param int $port The port number to check.
199 * @param bool $showWindow Whether to show a message box with the result.
200 * @return bool True if the port is in use by Memcached, false otherwise.
201 */
202 public function checkPort($port, $showWindow = false) {
203 global $bearsamppLang, $bearsamppWinbinder;
204 $boxTitle = sprintf($bearsamppLang->getValue(Lang::CHECK_PORT_TITLE), $this->getName(), $port);
205
206 if (!Util::isValidPort($port)) {
207 Util::logError($this->getName() . ' port not valid: ' . $port);
208 return false;
209 }
210
211 if (function_exists('memcache_connect')) {
212 $memcache = @memcache_connect('127.0.0.1', $port);
213 if ($memcache) {
214 $memcacheVersion = memcache_get_version($memcache);
215 Util::logDebug($this->getName() . ' port ' . $port . ' is used by: ' . $this->getName() . ' ' . $memcacheVersion);
216 memcache_close($memcache);
217 if ($showWindow) {
218 $bearsamppWinbinder->messageBoxInfo(
219 sprintf($bearsamppLang->getValue(Lang::PORT_USED_BY), $port, $this->getName() . ' ' . $memcacheVersion),
220 $boxTitle
221 );
222 }
223 return true;
224 }
225 } else {
226 $fp = @fsockopen('127.0.0.1', $port, $errno, $errstr, 3);
227 if (!$fp) {
228 Util::logDebug($this->getName() . ' port ' . $port . ' is used by another application');
229 if ($showWindow) {
230 $bearsamppWinbinder->messageBoxWarning(
231 sprintf($bearsamppLang->getValue(Lang::PORT_NOT_USED_BY), $port),
232 $boxTitle
233 );
234 }
235 } else {
236 Util::logDebug($this->getName() . ' port ' . $port . ' is not used');
237 if ($showWindow) {
238 $bearsamppWinbinder->messageBoxError(
239 sprintf($bearsamppLang->getValue(Lang::PORT_NOT_USED), $port),
240 $boxTitle
241 );
242 }
243 fclose($fp);
244 }
245 }
246
247 return false;
248 }
249
250 /**
251 * Switches the version of the Memcached service.
252 *
253 * @param string $version The version to switch to.
254 * @param bool $showWindow Whether to show a message box with the result.
255 * @return bool True if the version was successfully switched, false otherwise.
256 */
257 public function switchVersion($version, $showWindow = false) {
258 Util::logDebug('Switch ' . $this->name . ' version to ' . $version);
259 return $this->updateConfig($version, 0, $showWindow);
260 }
261
262 /**
263 * Updates the configuration for the Memcached service.
264 *
265 * @param string|null $version The version to update to. If null, the current version is used.
266 * @param int $sub The sub-level for logging indentation.
267 * @param bool $showWindow Whether to show a message box with the result.
268 * @return bool True if the configuration was successfully updated, false otherwise.
269 */
270 protected function updateConfig($version = null, $sub = 0, $showWindow = false) {
271 global $bearsamppLang, $bearsamppApps, $bearsamppWinbinder;
272
273 if (!$this->enable) {
274 return true;
275 }
276
277 $version = $version == null ? $this->version : $version;
278 Util::logDebug(($sub > 0 ? str_repeat(' ', 2 * $sub) : '') . 'Update ' . $this->name . ' ' . $version . ' config');
279
280 $boxTitle = sprintf($bearsamppLang->getValue(Lang::SWITCH_VERSION_TITLE), $this->getName(), $version);
281
282 $bearsamppConf = str_replace('memcached' . $this->getVersion(), 'memcached' . $version, $this->bearsamppConf);
283 if (!file_exists($bearsamppConf)) {
284 Util::logError('bearsampp config files not found for ' . $this->getName() . ' ' . $version);
285 if ($showWindow) {
286 $bearsamppWinbinder->messageBoxError(
287 sprintf($bearsamppLang->getValue(Lang::BEARSAMPP_CONF_NOT_FOUND_ERROR), $this->getName() . ' ' . $version),
288 $boxTitle
289 );
290 }
291 return false;
292 }
293
294 $bearsamppConfRaw = parse_ini_file($bearsamppConf);
295 if ($bearsamppConfRaw === false || !isset($bearsamppConfRaw[self::ROOT_CFG_VERSION]) || $bearsamppConfRaw[self::ROOT_CFG_VERSION] != $version) {
296 Util::logError('bearsampp config file malformed for ' . $this->getName() . ' ' . $version);
297 if ($showWindow) {
298 $bearsamppWinbinder->messageBoxError(
299 sprintf($bearsamppLang->getValue(Lang::BEARSAMPP_CONF_MALFORMED_ERROR), $this->getName() . ' ' . $version),
300 $boxTitle
301 );
302 }
303 return false;
304 }
305
306 // bearsampp.conf
307 $this->setVersion($version);
308
309 return true;
310 }
311
312 /**
313 * Sets the version of the Memcached service.
314 *
315 * @param string $version The version to set.
316 */
317 public function setVersion($version) {
318 global $bearsamppConfig;
319 $this->version = $version;
320 $bearsamppConfig->replace(self::ROOT_CFG_VERSION, $version);
321 $this->reload();
322 }
323
324 /**
325 * Retrieves the service object for the Memcached service.
326 *
327 * @return Win32Service The service object.
328 */
329 public function getService() {
330 return $this->service;
331 }
332
333 /**
334 * Enables or disables the Memcached service.
335 *
336 * @param bool $enabled Whether to enable or disable the service.
337 * @param bool $showWindow Whether to show a message box with the result.
338 */
339 public function setEnable($enabled, $showWindow = false) {
340 global $bearsamppConfig, $bearsamppLang, $bearsamppWinbinder;
341
342 if ($enabled == Config::ENABLED && !is_dir($this->currentPath)) {
343 Util::logDebug($this->getName() . ' cannot be enabled because bundle ' . $this->getVersion() . ' does not exist in ' . $this->currentPath);
344 if ($showWindow) {
345 $bearsamppWinbinder->messageBoxError(
346 sprintf($bearsamppLang->getValue(Lang::ENABLE_BUNDLE_NOT_EXIST), $this->getName(), $this->getVersion(), $this->currentPath),
347 sprintf($bearsamppLang->getValue(Lang::ENABLE_TITLE), $this->getName())
348 );
349 }
350 $enabled = Config::DISABLED;
351 }
352
353 Util::logInfo($this->getName() . ' switched to ' . ($enabled == Config::ENABLED ? 'enabled' : 'disabled'));
354 $this->enable = $enabled == Config::ENABLED;
355 $bearsamppConfig->replace(self::ROOT_CFG_ENABLE, $enabled);
356
357 $this->reload();
358 if ($this->enable) {
359 Util::installService($this, $this->port, null, $showWindow);
360 } else {
361 Util::removeService($this->service, $this->name);
362 }
363 }
364
365 /**
366 * Retrieves the log file path for the Memcached service.
367 *
368 * @return string The log file path.
369 */
370 public function getLog() {
371 return $this->log;
372 }
373
374 /**
375 * Retrieves the executable file path for the Memcached service.
376 *
377 * @return string The executable file path.
378 */
379 public function getExe() {
380 return $this->exe;
381 }
382
383 /**
384 * Retrieves the memory allocation for the Memcached service.
385 *
386 * @return int The memory allocation in MB.
387 */
388 public function getMemory() {
389 return $this->memory;
390 }
391
392 /**
393 * Sets the memory allocation for the Memcached service.
394 *
395 * @param int $memory The memory allocation in MB.
396 */
397 public function setMemory($memory) {
398 $this->replace(self::LOCAL_CFG_MEMORY, $memory);
399 }
400
401 /**
402 * Retrieves the port number for the Memcached service.
403 *
404 * @return int The port number.
405 */
406 public function getPort() {
407 return $this->port;
408 }
409
410 /**
411 * Sets the port number for the Memcached service.
412 *
413 * @param int $port The port number.
414 */
415 public function setPort($port) {
416 $this->replace(self::LOCAL_CFG_PORT, $port);
417 }
418}
global $bearsamppLang
global $bearsamppRoot
switchVersion($version, $showWindow=false)
setEnable($enabled, $showWindow=false)
changePort($port, $checkUsed=false, $wbProgressBar=null)
updateConfig($version=null, $sub=0, $showWindow=false)
reload($id=null, $type=null)
__construct($id, $type)
checkPort($port, $showWindow=false)
const DISABLED
const ENABLED
const ENABLE_TITLE
const PORT_NOT_USED_BY
const ERROR_FILE_NOT_FOUND
const MEMCACHED
const ERROR_INVALID_PARAMETER
const BEARSAMPP_CONF_NOT_FOUND_ERROR
const CHECK_PORT_TITLE
const ENABLE_BUNDLE_NOT_EXIST
const ERROR_CONF_NOT_FOUND
const SWITCH_VERSION_TITLE
const ERROR_EXE_NOT_FOUND
const PORT_USED_BY
const PORT_NOT_USED
const BEARSAMPP_CONF_MALFORMED_ERROR
update($sub=0, $showWindow=false)
replace($key, $value)
const SERVICE_DEMAND_START
const INFO_APP_PARAMETERS
const HKEY_LOCAL_MACHINE
static logReloadClass($classInstance)
static logError($data, $file=null)
static isValidPort($port)
static installService($bin, $port, $syntaxCheckCmd, $showWindow=false)
static removeService($service, $name)
static logDebug($data, $file=null)
static logInitClass($classInstance)
static isPortInUse($port)
static logInfo($data, $file=null)
global $bearsamppConfig
Definition homepage.php:26
const APP_TITLE
Definition root.php:12