PageRenderTime 50ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/Library/Command/Server.php

https://gitlab.com/robfrawley/php-memcached-admin
PHP | 513 lines | 249 code | 42 blank | 222 comment | 37 complexity | da95207d593485779111e668b4129a67 MD5 | raw file
  1. <?php
  2. /**
  3. * @author Cyrille Mahieux <elijaa(at)free.fr>
  4. * @copyright Copyright (c) 2010-2015, Cyrille Mahieux
  5. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0
  6. * @package phpMemcachedAdmin
  7. */
  8. /**
  9. * Sending command to memcache server
  10. *
  11. * @since 20/03/2010
  12. */
  13. class Library_Command_Server implements Library_Command_Interface
  14. {
  15. private static $_ini;
  16. private static $_log;
  17. /**
  18. * Constructor
  19. *
  20. * @param Array $ini Array from ini_parse
  21. *
  22. * @return void
  23. */
  24. public function __construct()
  25. {
  26. # Importing configuration
  27. self::$_ini = Library_Configuration_Loader::singleton();
  28. }
  29. /**
  30. * Executing a Command on a MemCache Server
  31. * With the help of http://github.com/memcached/memcached/blob/master/doc/protocol.txt
  32. * Return the response, or false otherwise
  33. *
  34. * @param String $command Command
  35. * @param String $server Server Hostname
  36. * @param Integer $port Server Port
  37. *
  38. * @return String|Boolean
  39. */
  40. public function exec($command, $server, $port)
  41. {
  42. # Variables
  43. $buffer = '';
  44. $handle = null;
  45. # Socket Opening
  46. if(!($handle = @fsockopen($server, $port, $errno, $errstr, self::$_ini->get('connection_timeout'))))
  47. {
  48. # Adding error to log
  49. self::$_log = utf8_encode($errstr);
  50. Library_Data_Error::add(utf8_encode($errstr));
  51. return false;
  52. }
  53. # Sending Command ...
  54. fwrite($handle, $command . "\r\n");
  55. # Getting first line
  56. $buffer = fgets($handle);
  57. # Checking if result is valid
  58. if($this->end($buffer, $command))
  59. {
  60. # Closing socket
  61. fclose($handle);
  62. # Adding error to log
  63. self::$_log = $buffer;
  64. return false;
  65. }
  66. # Reading Results
  67. while((!feof($handle)))
  68. {
  69. # Getting line
  70. $line = fgets($handle);
  71. $buffer .= $line;
  72. # Checking for end of MemCache command
  73. if($this->end($line, $command))
  74. {
  75. break;
  76. }
  77. }
  78. # Closing socket
  79. fclose($handle);
  80. return $buffer;
  81. }
  82. /**
  83. * Check if response is at the end from memcached server
  84. * Return true if response end, true otherwise
  85. *
  86. * @param String $buffer Buffer received from memcached server
  87. * @param String $command Command issued to memcached server
  88. *
  89. * @return Boolean
  90. */
  91. private function end($buffer, $command)
  92. {
  93. # incr or decr also return integer
  94. if((preg_match('/^(incr|decr)/', $command)))
  95. {
  96. if(preg_match('/^(END|ERROR|SERVER_ERROR|CLIENT_ERROR|NOT_FOUND|[0-9]*)/', $buffer))
  97. {
  98. return true;
  99. }
  100. }
  101. else
  102. {
  103. # Checking command response end
  104. if(preg_match('/^(END|DELETED|OK|ERROR|SERVER_ERROR|CLIENT_ERROR|NOT_FOUND|STORED|RESET|TOUCHED)/', $buffer))
  105. {
  106. return true;
  107. }
  108. }
  109. return false;
  110. }
  111. /**
  112. * Parse result to make an array
  113. *
  114. * @param String $string String to parse
  115. * @param Boolean $string (optional) Parsing stats ?
  116. *
  117. * @return Array
  118. */
  119. public function parse($string, $stats = true)
  120. {
  121. # Variable
  122. $return = array();
  123. # Exploding by \r\n
  124. $lines = preg_split('/\r\n/', $string);
  125. # Stats
  126. if($stats)
  127. {
  128. # Browsing each line
  129. foreach($lines as $line)
  130. {
  131. $data = preg_split('/ /', $line);
  132. if(isset($data[2]))
  133. {
  134. $return[$data[1]] = $data[2];
  135. }
  136. }
  137. }
  138. # Items
  139. else
  140. {
  141. # Browsing each line
  142. foreach($lines as $line)
  143. {
  144. $data = preg_split('/ /', $line);
  145. if(isset($data[1]))
  146. {
  147. $return[$data[1]] = array(substr($data[2], 1), $data[4]);
  148. }
  149. }
  150. }
  151. return $return;
  152. }
  153. /**
  154. * Send stats command to server
  155. * Return the result if successful or false otherwise
  156. *
  157. * @param String $server Hostname
  158. * @param Integer $port Hostname Port
  159. *
  160. * @return Array|Boolean
  161. */
  162. public function stats($server, $port)
  163. {
  164. # Executing command
  165. if(($return = $this->exec('stats', $server, $port)))
  166. {
  167. return $this->parse($return);
  168. }
  169. return false;
  170. }
  171. /**
  172. * Send stats settings command to server
  173. * Return the result if successful or false otherwise
  174. *
  175. * @param String $server Hostname
  176. * @param Integer $port Hostname Port
  177. *
  178. * @return Array|Boolean
  179. */
  180. public function settings($server, $port)
  181. {
  182. # Executing command
  183. if(($return = $this->exec('stats settings', $server, $port)))
  184. {
  185. return $this->parse($return);
  186. }
  187. return false;
  188. }
  189. /**
  190. * Send stats items command to server to retrieve slabs stats
  191. * Return the result if successful or false otherwise
  192. *
  193. * @param String $server Hostname
  194. * @param Integer $port Hostname Port
  195. *
  196. * @return Array|Boolean
  197. */
  198. public function slabs($server, $port)
  199. {
  200. # Initializing
  201. $slabs = array();
  202. # Finding uptime
  203. $stats = $this->stats($server, $port);
  204. $slabs['uptime'] = $stats['uptime'];
  205. unset($stats);
  206. # Executing command : slabs stats
  207. if(($result = $this->exec('stats slabs', $server, $port)))
  208. {
  209. # Parsing result
  210. $result = $this->parse($result);
  211. $slabs['active_slabs'] = $result['active_slabs'];
  212. $slabs['total_malloced'] = $result['total_malloced'];
  213. unset($result['active_slabs']);
  214. unset($result['total_malloced']);
  215. # Indexing by slabs
  216. foreach($result as $key => $value)
  217. {
  218. $key = preg_split('/:/', $key);
  219. $slabs[$key[0]][$key[1]] = $value;
  220. }
  221. # Executing command : items stats
  222. if(($result = $this->exec('stats items', $server, $port)))
  223. {
  224. # Parsing result
  225. $result = $this->parse($result);
  226. # Indexing by slabs
  227. foreach($result as $key => $value)
  228. {
  229. $key = preg_split('/:/', $key);
  230. $slabs[$key[1]]['items:' . $key[2]] = $value;
  231. }
  232. return $slabs;
  233. }
  234. }
  235. return false;
  236. }
  237. /**
  238. * Send stats cachedump command to server to retrieve slabs items
  239. * Return the result if successful or false otherwise
  240. *
  241. * @param String $server Hostname
  242. * @param Integer $port Hostname Port
  243. * @param Interger $slab Slab ID
  244. *
  245. * @return Array|Boolean
  246. */
  247. public function items($server, $port, $slab)
  248. {
  249. # Initializing
  250. $items = false;
  251. # Executing command : stats cachedump
  252. if(($result = $this->exec('stats cachedump ' . $slab . ' ' . self::$_ini->get('max_item_dump'), $server, $port)))
  253. {
  254. # Parsing result
  255. $items = $this->parse($result, false);
  256. }
  257. return $items;
  258. }
  259. /**
  260. * Send get command to server to retrieve an item
  261. * Return the result if successful or false otherwise
  262. *
  263. * @param String $server Hostname
  264. * @param Integer $port Hostname Port
  265. * @param String $key Key to retrieve
  266. *
  267. * @return String
  268. */
  269. public function get($server, $port, $key)
  270. {
  271. # Executing command : get
  272. if(($string = $this->exec('get ' . $key, $server, $port)))
  273. {
  274. $string = preg_replace('/^VALUE ' . preg_quote($key, '/') . '[0-9 ]*\r\n/', '', $string);
  275. if(ord($string[0]) == 0x78 && in_array(ord($string[1]), array(0x01,0x5e,0x9c,0xda))) {
  276. return gzuncompress($string);
  277. }
  278. return $string;
  279. }
  280. return self::$_log;
  281. }
  282. /**
  283. * Set an item
  284. * Return the result
  285. *
  286. * @param String $server Hostname
  287. * @param Integer $port Hostname Port
  288. * @param String $key Key to store
  289. * @param Mixed $data Data to store
  290. * @param Integer $duration Duration
  291. *
  292. * @return String
  293. */
  294. function set($server, $port, $key, $data, $duration)
  295. {
  296. # Formatting data
  297. $data = preg_replace('/\r/', '', $data);
  298. # Executing command : set
  299. if(($result = $this->exec('set ' . $key . ' 0 ' . $duration . ' ' . strlen($data) . "\r\n" . $data, $server, $port)))
  300. {
  301. return $result;
  302. }
  303. return self::$_log;
  304. }
  305. /**
  306. * Delete an item
  307. * Return true if successful, false otherwise
  308. *
  309. * @param String $server Hostname
  310. * @param Integer $port Hostname Port
  311. * @param String $key Key to delete
  312. *
  313. * @return String
  314. */
  315. public function delete($server, $port, $key)
  316. {
  317. # Executing command : delete
  318. if(($result = $this->exec('delete ' . $key, $server, $port)))
  319. {
  320. return $result;
  321. }
  322. return self::$_log;
  323. }
  324. /**
  325. * Increment the key by value
  326. * Return the result
  327. *
  328. * @param String $server Hostname
  329. * @param Integer $port Hostname Port
  330. * @param String $key Key to increment
  331. * @param Integer $value Value to increment
  332. *
  333. * @return String
  334. */
  335. function increment($server, $port, $key, $value)
  336. {
  337. # Executing command : increment
  338. if(($result = $this->exec('incr ' . $key . ' ' . $value, $server, $port)))
  339. {
  340. return $result;
  341. }
  342. return self::$_log;
  343. }
  344. /**
  345. * Decrement the key by value
  346. * Return the result
  347. *
  348. * @param String $server Hostname
  349. * @param Integer $port Hostname Port
  350. * @param String $key Key to decrement
  351. * @param Integer $value Value to decrement
  352. *
  353. * @return String
  354. */
  355. function decrement($server, $port, $key, $value)
  356. {
  357. # Executing command : decrement
  358. if(($result = $this->exec('decr ' . $key . ' ' . $value, $server, $port)))
  359. {
  360. return $result;
  361. }
  362. return self::$_log;
  363. }
  364. /**
  365. * Flush all items on a server
  366. * Return the result
  367. *
  368. * @param String $server Hostname
  369. * @param Integer $port Hostname Port
  370. * @param Integer $delay Delay before flushing server
  371. *
  372. * @return String
  373. */
  374. function flush_all($server, $port, $delay)
  375. {
  376. # Executing command : flush_all
  377. if(($result = $this->exec('flush_all ' . $delay, $server, $port)))
  378. {
  379. return $result;
  380. }
  381. return self::$_log;
  382. }
  383. /**
  384. * Search for item
  385. * Return all the items matching parameters if successful, false otherwise
  386. *
  387. * @param String $server Hostname
  388. * @param Integer $port Hostname Port
  389. * @param String $key Key to search
  390. * @param String $level Level of Detail
  391. * @param String $more More action
  392. *
  393. * @return array
  394. */
  395. function search($server, $port, $search, $level = false, $more = false)
  396. {
  397. $slabs = array();
  398. $items = false;
  399. # Executing command : stats
  400. if(($level == 'full') && ($result = $this->exec('stats', $server, $port)))
  401. {
  402. # Parsing result
  403. $result = $this->parse($result);
  404. $infinite = (isset($result['time'], $result['uptime'])) ? ($result['time'] - $result['uptime']) : 0;
  405. }
  406. # Executing command : slabs stats
  407. if(($result = $this->exec('stats slabs', $server, $port)))
  408. {
  409. # Parsing result
  410. $result = $this->parse($result);
  411. unset($result['active_slabs']);
  412. unset($result['total_malloced']);
  413. # Indexing by slabs
  414. foreach($result as $key => $value)
  415. {
  416. $key = preg_split('/:/', $key);
  417. $slabs[$key[0]] = true;
  418. }
  419. }
  420. # Exploring each slabs
  421. foreach($slabs as $slab => $unused)
  422. {
  423. # Executing command : stats cachedump
  424. if(($result = $this->exec('stats cachedump ' . $slab . ' 0', $server, $port)))
  425. {
  426. # Parsing result
  427. preg_match_all('/^ITEM ((?:.*)' . preg_quote($search, '/') . '(?:.*)) \[([0-9]*) b; ([0-9]*) s\]\r\n/imU', $result, $matchs, PREG_SET_ORDER);
  428. foreach($matchs as $item)
  429. {
  430. # Search & Delete
  431. if ($more == 'delete') {
  432. $items[] = $item[1] . ' : ' . $this->delete($server, $port, $item[1]);
  433. # Basic search
  434. } else {
  435. # Detail level
  436. if ($level == 'full') {
  437. $items[] = $item[1] . ' : [' . str_pad(Library_Data_Analysis::byteResize($item[2]), 7, ' ', STR_PAD_LEFT) . 'b, Expire : ' . (($item[3] == $infinite) ? '&#8734;': Library_Data_Analysis::uptime($item[3] - time(), true)) . ']';
  438. } else {
  439. $items[] = $item[1];
  440. }
  441. }
  442. }
  443. }
  444. unset($slabs[$slab]);
  445. }
  446. if(is_array($items))
  447. {
  448. sort($items);
  449. }
  450. return $items;
  451. }
  452. /**
  453. * Execute a telnet command on a server
  454. * Return the result
  455. *
  456. * @param String $server Hostname
  457. * @param Integer $port Hostname Port
  458. * @param String $command Command to execute
  459. *
  460. * @return String
  461. */
  462. function telnet($server, $port, $command)
  463. {
  464. # Executing command
  465. if(($result = $this->exec($command, $server, $port)))
  466. {
  467. return $result;
  468. }
  469. return self::$_log;
  470. }
  471. }