├── .gitattributes ├── .gitignore ├── Config └── Memcache.sample.php ├── LICENSE ├── Library ├── Command │ ├── Factory.php │ ├── Interface.php │ ├── Memcache.php │ ├── Memcached.php │ └── Server.php ├── Configuration │ └── Loader.php ├── Data │ ├── Analysis.php │ ├── Error.php │ └── Version.php ├── HTML │ └── Components.php └── Loader.php ├── Public ├── Scripts │ ├── Highcharts │ │ ├── highcharts.js │ │ └── standalone-framework.js │ └── script.js └── Styles │ └── Style.css ├── README.md ├── Temp └── .version ├── View ├── Commands │ └── Commands.phtml ├── Configure │ └── Configure.phtml ├── Footer.phtml ├── Header.phtml ├── LiveStats │ ├── Frame.phtml │ ├── Graphic.phtml │ └── Stats.phtml └── Stats │ ├── Error.phtml │ ├── Items.phtml │ ├── Slabs.phtml │ └── Stats.phtml ├── commands.php ├── composer.json ├── configure.php ├── index.php ├── spam.php └── stats.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Standards for multi-platform colaboration 2 | 3 | # Set default behaviour, in case users don't have core.autocrlf set. 4 | * text=auto 5 | *.sh eol=lf 6 | 7 | # Standard 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | 19 | 20 | # Denote all files that are truly binary and should not be modified. 21 | *.png binary 22 | *.jpg binary 23 | *.gif binary 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | *.7z 13 | *.dmg 14 | *.gz 15 | *.iso 16 | *.jar 17 | *.rar 18 | *.tar 19 | *.zip 20 | 21 | # Logs and databases # 22 | ###################### 23 | *.log 24 | *.sql 25 | *.sqlite 26 | 27 | # OS generated files # 28 | ###################### 29 | ._* 30 | .Spotlight-V100 31 | .Trashes 32 | Icon? 33 | ehthumbs.db 34 | *.rdb 35 | [Tt]humbs.db 36 | [Dd]esktop.ini 37 | *.DS_store 38 | .DS_store? 39 | 40 | # IDE files # 41 | ####################### 42 | *.swp 43 | *.swo 44 | *.pydevproject 45 | .project 46 | .metadata 47 | *.swp 48 | *~.nib 49 | local.properties 50 | .classpath 51 | .settings/ 52 | .loadpath 53 | .externalToolBuilders/ 54 | *.launch 55 | .cproject 56 | .buildpath 57 | nbproject/ 58 | .idea/ 59 | config.codekit 60 | *.LCK 61 | _notes/ 62 | dwsync.xml 63 | 64 | # Misc # 65 | ####################### 66 | bin/ 67 | tmp/ 68 | *.tmp 69 | *.bak 70 | *.[Cc]ache 71 | *.cpr 72 | *.orig 73 | *.php.in 74 | temp/ 75 | 76 | # Development files # 77 | ##################### 78 | build/**/* 79 | cache/**/* 80 | logs/* 81 | .svn* 82 | node_modules 83 | npm-debug.log 84 | .sass-cache 85 | !.bowerrc 86 | -------------------------------------------------------------------------------- /Config/Memcache.sample.php: -------------------------------------------------------------------------------- 1 | 'Server', 4 | 'slabs_api' => 'Server', 5 | 'items_api' => 'Server', 6 | 'get_api' => 'Server', 7 | 'set_api' => 'Server', 8 | 'delete_api' => 'Server', 9 | 'flush_all_api' => 'Server', 10 | 'connection_timeout' => '1', 11 | 'max_item_dump' => '100', 12 | 'refresh_rate' => 2, 13 | 'memory_alert' => '80', 14 | 'hit_rate_alert' => '90', 15 | 'eviction_alert' => '0', 16 | 'file_path' => 'Temp/', 17 | 'servers' => 18 | array ( 19 | 'Default' => 20 | array ( 21 | '127.0.0.1:11211' => 22 | array ( 23 | 'hostname' => '127.0.0.1', 24 | 'port' => '11211', 25 | ), 26 | ), 27 | ), 28 | ); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /Library/Command/Factory.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015-2017 WP-Cloud 5 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 6 | * @package phpMemcachedAdmin 7 | */ 8 | 9 | /** 10 | * Factory for communication with Memcache Server 11 | * 12 | * @since 30/03/2010 13 | */ 14 | class Library_Command_Factory 15 | { 16 | private static $_object = array(); 17 | 18 | # No explicit call of constructor 19 | private function __construct() {} 20 | 21 | # No explicit call of clone() 22 | private function __clone() {} 23 | 24 | /** 25 | * Accessor to command class instance by command type 26 | * 27 | * @param String $command Type of command 28 | * 29 | * @return void 30 | */ 31 | public static function instance($command) 32 | { 33 | # Importing configuration 34 | $_ini = Library_Configuration_Loader::singleton(); 35 | 36 | # Instance does not exists 37 | if(!isset(self::$_object[$_ini->get($command)]) || ($_ini->get($command) != 'Server')) 38 | { 39 | # Switching by API 40 | switch($_ini->get($command)) 41 | { 42 | case 'Memcache': 43 | # PECL Memcache API 44 | self::$_object['Memcache'] = new Library_Command_Memcache(); 45 | break; 46 | 47 | case 'Memcached': 48 | # PECL Memcached API 49 | self::$_object['Memcached'] = new Library_Command_Memcached(); 50 | break; 51 | 52 | case 'Server': 53 | default: 54 | # Server API (eg communicating directly with the memcache server) 55 | self::$_object['Server'] = new Library_Command_Server(); 56 | break; 57 | } 58 | } 59 | return self::$_object[$_ini->get($command)]; 60 | } 61 | 62 | /** 63 | * Accessor to command class instance by type 64 | * 65 | * @param String $api Type of command 66 | * 67 | * @return void 68 | */ 69 | public static function api($api) 70 | { 71 | # Instance does not exists 72 | if(!isset(self::$_object[$api]) || ($api != 'Server')) 73 | { 74 | # Switching by API 75 | switch($api) 76 | { 77 | case 'Memcache': 78 | # PECL Memcache API 79 | self::$_object['Memcache'] = new Library_Command_Memcache(); 80 | break; 81 | 82 | case 'Memcached': 83 | # PECL Memcached API 84 | self::$_object['Memcached'] = new Library_Command_Memcached(); 85 | break; 86 | 87 | case 'Server': 88 | default: 89 | # Server API (eg communicating directly with the memcache server) 90 | self::$_object['Server'] = new Library_Command_Server(); 91 | break; 92 | } 93 | } 94 | return self::$_object[$api]; 95 | } 96 | } -------------------------------------------------------------------------------- /Library/Command/Interface.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015-2017 WP-Cloud 5 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 6 | * @package phpMemcachedAdmin 7 | */ 8 | 9 | /** 10 | * Interface of communication to MemCache Server 11 | * 12 | * @since 20/03/2010 13 | */ 14 | interface Library_Command_Interface 15 | { 16 | /** 17 | * Constructor 18 | * 19 | * @return void 20 | */ 21 | function __construct(); 22 | 23 | /** 24 | * Send stats command to server 25 | * Return the result if successful or false otherwise 26 | * 27 | * @param String $server Hostname 28 | * @param Integer $port Hostname Port 29 | * 30 | * @return Array|Boolean 31 | */ 32 | function stats($server, $port); 33 | 34 | /** 35 | * Send stats settings command to server 36 | * Return the result if successful or false otherwise 37 | * 38 | * @param String $server Hostname 39 | * @param Integer $port Hostname Port 40 | * 41 | * @return Array|Boolean 42 | */ 43 | public function settings($server, $port); 44 | 45 | /** 46 | * Retrieve slabs stats 47 | * Return the result if successful or false otherwise 48 | * 49 | * @param String $server Hostname 50 | * @param Integer $port Hostname Port 51 | * 52 | * @return Array|Boolean 53 | */ 54 | function slabs($server, $port); 55 | 56 | /** 57 | * Retrieve items from a slabs 58 | * Return the result if successful or false otherwise 59 | * 60 | * @param String $server Hostname 61 | * @param Integer $port Hostname Port 62 | * @param Integer $slab Slab ID 63 | * 64 | * @return Array|Boolean 65 | */ 66 | function items($server, $port, $slab); 67 | 68 | /** 69 | * Send get command to server to retrieve an item 70 | * Return the result 71 | * 72 | * @param String $server Hostname 73 | * @param Integer $port Hostname Port 74 | * @param String $key Key to retrieve 75 | * 76 | * @return String 77 | */ 78 | function get($server, $port, $key); 79 | 80 | /** 81 | * Set an item 82 | * Return the result 83 | * 84 | * @param String $server Hostname 85 | * @param Integer $port Hostname Port 86 | * @param String $key Key to store 87 | * @param Mixed $data Data to store 88 | * @param Integer $duration Duration 89 | * 90 | * @return String 91 | */ 92 | function set($server, $port, $key, $data, $duration); 93 | 94 | /** 95 | * Delete an item 96 | * Return the result 97 | * 98 | * @param String $server Hostname 99 | * @param Integer $port Hostname Port 100 | * @param String $key Key to delete 101 | * 102 | * @return String 103 | */ 104 | function delete($server, $port, $key); 105 | 106 | /** 107 | * Increment the key by value 108 | * Return the result 109 | * 110 | * @param String $server Hostname 111 | * @param Integer $port Hostname Port 112 | * @param String $key Key to increment 113 | * @param Integer $value Value to increment 114 | * 115 | * @return String 116 | */ 117 | function increment($server, $port, $key, $value); 118 | 119 | /** 120 | * Decrement the key by value 121 | * Return the result 122 | * 123 | * @param String $server Hostname 124 | * @param Integer $port Hostname Port 125 | * @param String $key Key to decrement 126 | * @param Integer $value Value to decrement 127 | * 128 | * @return String 129 | */ 130 | function decrement($server, $port, $key, $value); 131 | 132 | /** 133 | * Flush all items on a server after delay 134 | * Return the result 135 | * 136 | * @param String $server Hostname 137 | * @param Integer $port Hostname Port 138 | * @param Integer $delay Delay before flushing server 139 | * 140 | * @return String 141 | */ 142 | function flush_all($server, $port, $delay); 143 | 144 | /** 145 | * Search for item 146 | * Return all the items matching parameters if successful, false otherwise 147 | * 148 | * @param String $server Hostname 149 | * @param Integer $port Hostname Port 150 | * @param String $key Key to search 151 | * @param Boolean $level Level of detail 152 | * @param Boolean $more More action 153 | * 154 | * @return array 155 | */ 156 | function search($server, $port, $search, $level = false, $more = false); 157 | 158 | /** 159 | * Execute a telnet command on a server 160 | * Return the result 161 | * 162 | * @param String $server Hostname 163 | * @param Integer $port Hostname Port 164 | * @param String $command Command to execute 165 | * 166 | * @return String 167 | */ 168 | function telnet($server, $port, $command); 169 | } -------------------------------------------------------------------------------- /Library/Command/Memcache.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015-2017 WP-Cloud 5 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 6 | * @package phpMemcachedAdmin 7 | */ 8 | 9 | /** 10 | * Sending command to memcache server via PECL memcache API http://pecl.php.net/package/memcache 11 | * 12 | * @since 20/03/2010 13 | */ 14 | class Library_Command_Memcache implements Library_Command_Interface 15 | { 16 | private static $_ini; 17 | private static $_memcache; 18 | 19 | /** 20 | * Constructor 21 | * 22 | * @param Array $ini Array from ini_parse 23 | * 24 | * @return void 25 | */ 26 | public function __construct() 27 | { 28 | # Importing configuration 29 | self::$_ini = Library_Configuration_Loader::singleton(); 30 | 31 | # Initializing 32 | self::$_memcache = new Memcache(); 33 | } 34 | 35 | /** 36 | * Send stats command to server 37 | * Return the result if successful or false otherwise 38 | * 39 | * @param String $server Hostname 40 | * @param Integer $port Hostname Port 41 | * 42 | * @return Array|Boolean 43 | */ 44 | public function stats($server, $port) 45 | { 46 | # Adding server 47 | self::$_memcache->addServer($server, $port); 48 | 49 | # Executing command 50 | if(($return = self::$_memcache->getExtendedStats())) 51 | { 52 | # Delete server key based 53 | $stats = $return[$server.':'.$port]; 54 | return $stats; 55 | } 56 | return false; 57 | } 58 | 59 | /** 60 | * Send stats settings command to server 61 | * Return the result if successful or false otherwise 62 | * 63 | * @param String $server Hostname 64 | * @param Integer $port Hostname Port 65 | * 66 | * @return Array|Boolean 67 | */ 68 | public function settings($server, $port) 69 | { 70 | return false; 71 | } 72 | 73 | /** 74 | * Send stats items command to server to retrieve slabs stats 75 | * Return the result if successful or false otherwise 76 | * 77 | * @param String $server Hostname 78 | * @param Integer $port Hostname Port 79 | * 80 | * @return Array|Boolean 81 | */ 82 | public function slabs($server, $port) 83 | { 84 | # Initializing 85 | $slabs = array(); 86 | 87 | # Adding server 88 | self::$_memcache->addServer($server, $port); 89 | 90 | # Executing command : slabs 91 | if(($slabs = self::$_memcache->getStats('slabs'))) 92 | { 93 | # Finding uptime 94 | $stats = $this->stats($server, $port); 95 | $slabs['uptime'] = $stats['uptime']; 96 | unset($stats); 97 | 98 | # Executing command : items 99 | if(($result = self::$_memcache->getStats('items'))) 100 | { 101 | # Indexing by slabs 102 | foreach($result['items'] as $id => $items) 103 | { 104 | foreach($items as $key => $value) 105 | { 106 | $slabs[$id]['items:' . $key] = $value; 107 | } 108 | } 109 | return $slabs; 110 | } 111 | } 112 | return false; 113 | } 114 | 115 | /** 116 | * Send stats cachedump command to server to retrieve slabs items 117 | * Return the result if successful or false otherwise 118 | * 119 | * @param String $server Hostname 120 | * @param Integer $port Hostname Port 121 | * @param Interger $slab Slab ID 122 | * 123 | * @return Array|Boolean 124 | */ 125 | public function items($server, $port, $slab) 126 | { 127 | # Initializing 128 | $items = false; 129 | 130 | # Adding server 131 | self::$_memcache->addServer($server, $port); 132 | 133 | # Executing command : slabs stats 134 | if(($items = self::$_memcache->getStats('cachedump', $slab, self::$_ini->get('max_item_dump')))) 135 | { 136 | return $items; 137 | } 138 | return false; 139 | } 140 | 141 | /** 142 | * Send get command to server to retrieve an item 143 | * Return the result 144 | * 145 | * @param String $server Hostname 146 | * @param Integer $port Hostname Port 147 | * @param String $key Key to retrieve 148 | * 149 | * @return String 150 | */ 151 | public function get($server, $port, $key) 152 | { 153 | # Adding server 154 | self::$_memcache->addServer($server, $port); 155 | 156 | # Executing command : get 157 | if($item = self::$_memcache->get($key)) 158 | { 159 | return print_r($item, true); 160 | } 161 | return 'NOT_FOUND'; 162 | } 163 | 164 | /** 165 | * Set an item 166 | * Return the result 167 | * 168 | * @param String $server Hostname 169 | * @param Integer $port Hostname Port 170 | * @param String $key Key to store 171 | * @param Mixed $data Data to store 172 | * @param Integer $duration Duration 173 | * 174 | * @return String 175 | */ 176 | function set($server, $port, $key, $data, $duration) 177 | { 178 | # Adding server 179 | self::$_memcache->addServer($server, $port); 180 | 181 | # Executing command : set 182 | if(self::$_memcache->set($key, $data, 0, $duration)) 183 | { 184 | return 'STORED'; 185 | } 186 | return 'ERROR'; 187 | } 188 | 189 | /** 190 | * Delete an item 191 | * Return the result 192 | * 193 | * @param String $server Hostname 194 | * @param Integer $port Hostname Port 195 | * @param String $key Key to delete 196 | * 197 | * @return String 198 | */ 199 | public function delete($server, $port, $key) 200 | { 201 | # Adding server 202 | self::$_memcache->addServer($server, $port); 203 | 204 | # Executing command : delete 205 | if(self::$_memcache->delete($key)) 206 | { 207 | return 'DELETED'; 208 | } 209 | return 'NOT_FOUND'; 210 | } 211 | 212 | /** 213 | * Increment the key by value 214 | * Return the result 215 | * 216 | * @param String $server Hostname 217 | * @param Integer $port Hostname Port 218 | * @param String $key Key to increment 219 | * @param Integer $value Value to increment 220 | * 221 | * @return String 222 | */ 223 | function increment($server, $port, $key, $value) 224 | { 225 | # Adding server 226 | self::$_memcache->addServer($server, $port); 227 | 228 | # Executing command : increment 229 | if($result = self::$_memcache->increment($key, $value)) 230 | { 231 | return $result; 232 | } 233 | return 'NOT_FOUND'; 234 | } 235 | 236 | /** 237 | * Decrement the key by value 238 | * Return the result 239 | * 240 | * @param String $server Hostname 241 | * @param Integer $port Hostname Port 242 | * @param String $key Key to decrement 243 | * @param Integer $value Value to decrement 244 | * 245 | * @return String 246 | */ 247 | function decrement($server, $port, $key, $value) 248 | { 249 | # Adding server 250 | self::$_memcache->addServer($server, $port); 251 | 252 | # Executing command : decrement 253 | if($result = self::$_memcache->decrement($key, $value)) 254 | { 255 | return $result; 256 | } 257 | return 'NOT_FOUND'; 258 | } 259 | 260 | /** 261 | * Flush all items on a server 262 | * Warning, delay won't work with Memcache API 263 | * Return the result 264 | * 265 | * @param String $server Hostname 266 | * @param Integer $port Hostname Port 267 | * @param Integer $delay Delay before flushing server 268 | * 269 | * @return String 270 | */ 271 | function flush_all($server, $port, $delay) 272 | { 273 | # Adding server 274 | self::$_memcache->addServer($server, $port); 275 | 276 | # Executing command : flush_all 277 | self::$_memcache->flush(); 278 | return 'OK'; 279 | } 280 | 281 | /** 282 | * Search for item 283 | * Return all the items matching parameters if successful, false otherwise 284 | * 285 | * @param String $server Hostname 286 | * @param Integer $port Hostname Port 287 | * @param String $key Key to search 288 | * 289 | * @return array 290 | */ 291 | function search($server, $port, $search, $level = false, $more = false) 292 | { 293 | throw new Exception('PECL Memcache does not support search function, use Server instead'); 294 | } 295 | 296 | /** 297 | * Execute a telnet command on a server 298 | * Return the result 299 | * 300 | * @param String $server Hostname 301 | * @param Integer $port Hostname Port 302 | * @param String $command Command to execute 303 | * 304 | * @return String 305 | */ 306 | function telnet($server, $port, $command) 307 | { 308 | throw new Exception('PECL Memcache does not support telnet, use Server instead'); 309 | } 310 | } -------------------------------------------------------------------------------- /Library/Command/Memcached.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015-2017 WP-Cloud 5 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 6 | * @package phpMemcachedAdmin 7 | */ 8 | 9 | /** 10 | * Sending command to memcache server via PECL memcache API http://pecl.php.net/package/memcache 11 | * 12 | * @since 20/03/2010 13 | */ 14 | class Library_Command_Memcached implements Library_Command_Interface 15 | { 16 | private static $_ini; 17 | private static $_memcache; 18 | 19 | /** 20 | * Constructor 21 | * 22 | * @param Array $ini Array from ini_parse 23 | * 24 | * @return void 25 | */ 26 | public function __construct() 27 | { 28 | # Importing configuration 29 | self::$_ini = Library_Configuration_Loader::singleton(); 30 | 31 | # Initializing 32 | self::$_memcache = new Memcached(); 33 | } 34 | 35 | /** 36 | * Send stats command to server 37 | * Return the result if successful or false otherwise 38 | * 39 | * @param String $server Hostname 40 | * @param Integer $port Hostname Port 41 | * 42 | * @return Array|Boolean 43 | */ 44 | public function stats($server, $port) 45 | { 46 | # Adding server 47 | self::$_memcache->addServer($server, $port); 48 | 49 | # Executing command 50 | if(($return = self::$_memcache->getStats())) 51 | { 52 | # Delete server key based 53 | $stats = $return[$server.':'.$port]; 54 | 55 | # Adding value that miss 56 | $stats['delete_hits'] = ''; 57 | $stats['delete_misses'] = ''; 58 | $stats['incr_hits'] = ''; 59 | $stats['incr_misses'] = ''; 60 | $stats['decr_hits'] = ''; 61 | $stats['decr_misses'] = ''; 62 | $stats['cas_hits'] = ''; 63 | $stats['cas_misses'] = ''; 64 | $stats['cas_badval'] = ''; 65 | 66 | return $stats; 67 | } 68 | return false; 69 | } 70 | 71 | /** 72 | * Send stats settings command to server 73 | * Return the result if successful or false otherwise 74 | * 75 | * @param String $server Hostname 76 | * @param Integer $port Hostname Port 77 | * 78 | * @return Array|Boolean 79 | */ 80 | public function settings($server, $port) 81 | { 82 | return false; 83 | } 84 | 85 | /** 86 | * Send stats items command to server to retrieve slabs stats 87 | * Return the result if successful or false otherwise 88 | * 89 | * @param String $server Hostname 90 | * @param Integer $port Hostname Port 91 | * 92 | * @return Array|Boolean 93 | */ 94 | public function slabs($server, $port) 95 | { 96 | throw new Exception('PECL Memcache does not support slabs stats, use Server or Memcache instead'); 97 | } 98 | 99 | /** 100 | * Send stats cachedump command to server to retrieve slabs items 101 | * Return the result if successful or false otherwise 102 | * 103 | * @param String $server Hostname 104 | * @param Integer $port Hostname Port 105 | * @param Interger $slab Slab ID 106 | * 107 | * @return Array|Boolean 108 | */ 109 | public function items($server, $port, $slab) 110 | { 111 | throw new Exception('PECL Memcache does not support slabs items stats, use Server or Memcache instead'); 112 | } 113 | 114 | /** 115 | * Send get command to server to retrieve an item 116 | * Return the result 117 | * 118 | * @param String $server Hostname 119 | * @param Integer $port Hostname Port 120 | * @param String $key Key to retrieve 121 | * 122 | * @return String 123 | */ 124 | public function get($server, $port, $key) 125 | { 126 | # Adding server 127 | self::$_memcache->addServer($server, $port); 128 | 129 | # Executing command : get 130 | if($item = self::$_memcache->get($key)) 131 | { 132 | return print_r($item, true); 133 | } 134 | return self::$_memcache->getResultMessage(); 135 | } 136 | 137 | /** 138 | * Set an item 139 | * Return the result 140 | * 141 | * @param String $server Hostname 142 | * @param Integer $port Hostname Port 143 | * @param String $key Key to store 144 | * @param Mixed $data Data to store 145 | * @param Integer $duration Duration 146 | * 147 | * @return String 148 | */ 149 | function set($server, $port, $key, $data, $duration) 150 | { 151 | # Adding server 152 | self::$_memcache->addServer($server, $port); 153 | 154 | # Checking duration 155 | if($duration == '') { $duration = 0; } 156 | 157 | # Executing command : set 158 | self::$_memcache->set($key, $data, $duration); 159 | return self::$_memcache->getResultMessage(); 160 | } 161 | 162 | /** 163 | * Delete an item 164 | * Return the result 165 | * 166 | * @param String $server Hostname 167 | * @param Integer $port Hostname Port 168 | * @param String $key Key to delete 169 | * 170 | * @return String 171 | */ 172 | public function delete($server, $port, $key) 173 | { 174 | # Adding server 175 | self::$_memcache->addServer($server, $port); 176 | 177 | # Executing command : delete 178 | self::$_memcache->delete($key); 179 | return self::$_memcache->getResultMessage(); 180 | } 181 | 182 | /** 183 | * Increment the key by value 184 | * Return the result 185 | * 186 | * @param String $server Hostname 187 | * @param Integer $port Hostname Port 188 | * @param String $key Key to increment 189 | * @param Integer $value Value to increment 190 | * 191 | * @return String 192 | */ 193 | function increment($server, $port, $key, $value) 194 | { 195 | # Adding server 196 | self::$_memcache->addServer($server, $port); 197 | 198 | # Executing command : increment 199 | if($result = self::$_memcache->increment($key, $value)) 200 | { 201 | return $result; 202 | } 203 | return self::$_memcache->getResultMessage(); 204 | } 205 | 206 | /** 207 | * Decrement the key by value 208 | * Return the result 209 | * 210 | * @param String $server Hostname 211 | * @param Integer $port Hostname Port 212 | * @param String $key Key to decrement 213 | * @param Integer $value Value to decrement 214 | * 215 | * @return String 216 | */ 217 | function decrement($server, $port, $key, $value) 218 | { 219 | # Adding server 220 | self::$_memcache->addServer($server, $port); 221 | 222 | # Executing command : decrement 223 | if($result = self::$_memcache->decrement($key, $value)) 224 | { 225 | return $result; 226 | } 227 | return self::$_memcache->getResultMessage(); 228 | } 229 | 230 | /** 231 | * Flush all items on a server 232 | * Return the result 233 | * 234 | * @param String $server Hostname 235 | * @param Integer $port Hostname Port 236 | * @param Integer $delay Delay before flushing server 237 | * 238 | * @return String 239 | */ 240 | public function flush_all($server, $port, $delay) 241 | { 242 | # Adding server 243 | self::$_memcache->addServer($server, $port); 244 | 245 | # Executing command : delete 246 | self::$_memcache->flush($delay); 247 | return self::$_memcache->getResultMessage(); 248 | } 249 | 250 | /** 251 | * Search for item 252 | * Return all the items matching parameters if successful, false otherwise 253 | * 254 | * @param String $server Hostname 255 | * @param Integer $port Hostname Port 256 | * @param String $key Key to search 257 | * 258 | * @return Array 259 | */ 260 | function search($server, $port, $search, $level = false, $more = false) 261 | { 262 | throw new Exception('PECL Memcached does not support search function, use Server instead'); 263 | } 264 | 265 | /** 266 | * Execute a telnet command on a server 267 | * Return the result 268 | * 269 | * @param String $server Hostname 270 | * @param Integer $port Hostname Port 271 | * @param String $command Command to execute 272 | * 273 | * @return String 274 | */ 275 | function telnet($server, $port, $command) 276 | { 277 | throw new Exception('PECL Memcached does not support telnet, use Server instead'); 278 | } 279 | } -------------------------------------------------------------------------------- /Library/Command/Server.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015-2017 WP-Cloud 5 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 6 | * @package phpMemcachedAdmin 7 | */ 8 | 9 | /** 10 | * Sending command to memcache server 11 | * 12 | * @since 20/03/2010 13 | */ 14 | class Library_Command_Server implements Library_Command_Interface 15 | { 16 | private static $_ini; 17 | private static $_log; 18 | 19 | /** 20 | * Constructor 21 | * 22 | * @param Array $ini Array from ini_parse 23 | * 24 | * @return void 25 | */ 26 | public function __construct() 27 | { 28 | # Importing configuration 29 | self::$_ini = Library_Configuration_Loader::singleton(); 30 | } 31 | 32 | /** 33 | * Executing a Command on a MemCache Server 34 | * With the help of http://github.com/memcached/memcached/blob/master/doc/protocol.txt 35 | * Return the response, or false otherwise 36 | * 37 | * @param String $command Command 38 | * @param String $server Server Hostname 39 | * @param Integer $port Server Port 40 | * 41 | * @return String|Boolean 42 | */ 43 | public function exec($command, $server, $port) 44 | { 45 | # Variables 46 | $buffer = ''; 47 | $handle = null; 48 | 49 | # Socket Opening 50 | if(!($handle = @fsockopen($server, $port, $errno, $errstr, self::$_ini->get('connection_timeout')))) 51 | { 52 | # Adding error to log 53 | self::$_log = utf8_encode($errstr); 54 | Library_Data_Error::add(utf8_encode($errstr)); 55 | return false; 56 | } 57 | 58 | # Sending Command ... 59 | fwrite($handle, $command . "\r\n"); 60 | 61 | # Getting first line 62 | $buffer = fgets($handle); 63 | 64 | # Checking if result is valid 65 | if($this->end($buffer, $command)) 66 | { 67 | # Closing socket 68 | fclose($handle); 69 | 70 | # Adding error to log 71 | self::$_log = $buffer; 72 | 73 | return false; 74 | } 75 | 76 | # Reading Results 77 | while((!feof($handle))) 78 | { 79 | # Getting line 80 | $line = fgets($handle); 81 | 82 | $buffer .= $line; 83 | 84 | # Checking for end of MemCache command 85 | if($this->end($line, $command)) 86 | { 87 | break; 88 | } 89 | } 90 | # Closing socket 91 | fclose($handle); 92 | 93 | return $buffer; 94 | } 95 | 96 | /** 97 | * Check if response is at the end from memcached server 98 | * Return true if response end, true otherwise 99 | * 100 | * @param String $buffer Buffer received from memcached server 101 | * @param String $command Command issued to memcached server 102 | * 103 | * @return Boolean 104 | */ 105 | private function end($buffer, $command) 106 | { 107 | # incr or decr also return integer 108 | if((preg_match('/^(incr|decr)/', $command))) 109 | { 110 | if(preg_match('/^(END|ERROR|SERVER_ERROR|CLIENT_ERROR|NOT_FOUND|[0-9]*)/', $buffer)) 111 | { 112 | return true; 113 | } 114 | } 115 | else 116 | { 117 | # Checking command response end 118 | if(preg_match('/^(END|DELETED|OK|ERROR|SERVER_ERROR|CLIENT_ERROR|NOT_FOUND|STORED|RESET|TOUCHED)/', $buffer)) 119 | { 120 | return true; 121 | } 122 | } 123 | return false; 124 | } 125 | 126 | /** 127 | * Parse result to make an array 128 | * 129 | * @param String $string String to parse 130 | * @param Boolean $string (optional) Parsing stats ? 131 | * 132 | * @return Array 133 | */ 134 | public function parse($string, $stats = true) 135 | { 136 | # Variable 137 | $return = array(); 138 | 139 | # Exploding by \r\n 140 | $lines = preg_split('/\r\n/', $string); 141 | 142 | # Stats 143 | if($stats) 144 | { 145 | # Browsing each line 146 | foreach($lines as $line) 147 | { 148 | $data = preg_split('/ /', $line); 149 | if(isset($data[2])) 150 | { 151 | $return[$data[1]] = $data[2]; 152 | } 153 | } 154 | } 155 | # Items 156 | else 157 | { 158 | # Browsing each line 159 | foreach($lines as $line) 160 | { 161 | $data = preg_split('/ /', $line); 162 | if(isset($data[1])) 163 | { 164 | $return[$data[1]] = array(substr($data[2], 1), $data[4]); 165 | } 166 | } 167 | } 168 | return $return; 169 | } 170 | 171 | /** 172 | * Send stats 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 stats($server, $port) 181 | { 182 | # Executing command 183 | if(($return = $this->exec('stats', $server, $port))) 184 | { 185 | return $this->parse($return); 186 | } 187 | return false; 188 | } 189 | 190 | /** 191 | * Send stats settings command to server 192 | * Return the result if successful or false otherwise 193 | * 194 | * @param String $server Hostname 195 | * @param Integer $port Hostname Port 196 | * 197 | * @return Array|Boolean 198 | */ 199 | public function settings($server, $port) 200 | { 201 | # Executing command 202 | if(($return = $this->exec('stats settings', $server, $port))) 203 | { 204 | return $this->parse($return); 205 | } 206 | return false; 207 | } 208 | 209 | /** 210 | * Send stats items command to server to retrieve slabs stats 211 | * Return the result if successful or false otherwise 212 | * 213 | * @param String $server Hostname 214 | * @param Integer $port Hostname Port 215 | * 216 | * @return Array|Boolean 217 | */ 218 | public function slabs($server, $port) 219 | { 220 | # Initializing 221 | $slabs = array(); 222 | 223 | # Finding uptime 224 | $stats = $this->stats($server, $port); 225 | $slabs['uptime'] = $stats['uptime']; 226 | unset($stats); 227 | 228 | # Executing command : slabs stats 229 | if(($result = $this->exec('stats slabs', $server, $port))) 230 | { 231 | # Parsing result 232 | $result = $this->parse($result); 233 | $slabs['active_slabs'] = $result['active_slabs']; 234 | $slabs['total_malloced'] = $result['total_malloced']; 235 | unset($result['active_slabs']); 236 | unset($result['total_malloced']); 237 | 238 | # Indexing by slabs 239 | foreach($result as $key => $value) 240 | { 241 | $key = preg_split('/:/', $key); 242 | $slabs[$key[0]][$key[1]] = $value; 243 | } 244 | 245 | # Executing command : items stats 246 | if(($result = $this->exec('stats items', $server, $port))) 247 | { 248 | # Parsing result 249 | $result = $this->parse($result); 250 | 251 | # Indexing by slabs 252 | foreach($result as $key => $value) 253 | { 254 | $key = preg_split('/:/', $key); 255 | $slabs[$key[1]]['items:' . $key[2]] = $value; 256 | } 257 | 258 | return $slabs; 259 | } 260 | } 261 | return false; 262 | } 263 | 264 | /** 265 | * Send stats cachedump command to server to retrieve slabs items 266 | * Return the result if successful or false otherwise 267 | * 268 | * @param String $server Hostname 269 | * @param Integer $port Hostname Port 270 | * @param Interger $slab Slab ID 271 | * 272 | * @return Array|Boolean 273 | */ 274 | public function items($server, $port, $slab) 275 | { 276 | # Initializing 277 | $items = false; 278 | 279 | # Executing command : stats cachedump 280 | if(($result = $this->exec('stats cachedump ' . $slab . ' ' . self::$_ini->get('max_item_dump'), $server, $port))) 281 | { 282 | # Parsing result 283 | $items = $this->parse($result, false); 284 | } 285 | return $items; 286 | } 287 | 288 | /** 289 | * Send get command to server to retrieve an item 290 | * Return the result if successful or false otherwise 291 | * 292 | * @param String $server Hostname 293 | * @param Integer $port Hostname Port 294 | * @param String $key Key to retrieve 295 | * 296 | * @return String 297 | */ 298 | public function get($server, $port, $key) 299 | { 300 | # Executing command : get 301 | if(($string = $this->exec('get ' . $key, $server, $port))) 302 | { 303 | $string = preg_replace('/^VALUE ' . preg_quote($key, '/') . '[0-9 ]*\r\n/', '', $string); 304 | if(ord($string[0]) == 0x78 && in_array(ord($string[1]), array(0x01,0x5e,0x9c,0xda))) { 305 | return gzuncompress($string); 306 | } 307 | return $string; 308 | } 309 | return self::$_log; 310 | } 311 | 312 | /** 313 | * Set an item 314 | * Return the result 315 | * 316 | * @param String $server Hostname 317 | * @param Integer $port Hostname Port 318 | * @param String $key Key to store 319 | * @param Mixed $data Data to store 320 | * @param Integer $duration Duration 321 | * 322 | * @return String 323 | */ 324 | function set($server, $port, $key, $data, $duration) 325 | { 326 | # Formatting data 327 | $data = preg_replace('/\r/', '', $data); 328 | 329 | # Executing command : set 330 | if(($result = $this->exec('set ' . $key . ' 0 ' . $duration . ' ' . strlen($data) . "\r\n" . $data, $server, $port))) 331 | { 332 | return $result; 333 | } 334 | return self::$_log; 335 | } 336 | 337 | /** 338 | * Delete an item 339 | * Return true if successful, false otherwise 340 | * 341 | * @param String $server Hostname 342 | * @param Integer $port Hostname Port 343 | * @param String $key Key to delete 344 | * 345 | * @return String 346 | */ 347 | public function delete($server, $port, $key) 348 | { 349 | # Executing command : delete 350 | if(($result = $this->exec('delete ' . $key, $server, $port))) 351 | { 352 | return $result; 353 | } 354 | return self::$_log; 355 | } 356 | 357 | /** 358 | * Increment the key by value 359 | * Return the result 360 | * 361 | * @param String $server Hostname 362 | * @param Integer $port Hostname Port 363 | * @param String $key Key to increment 364 | * @param Integer $value Value to increment 365 | * 366 | * @return String 367 | */ 368 | function increment($server, $port, $key, $value) 369 | { 370 | # Executing command : increment 371 | if(($result = $this->exec('incr ' . $key . ' ' . $value, $server, $port))) 372 | { 373 | return $result; 374 | } 375 | return self::$_log; 376 | } 377 | 378 | /** 379 | * Decrement the key by value 380 | * Return the result 381 | * 382 | * @param String $server Hostname 383 | * @param Integer $port Hostname Port 384 | * @param String $key Key to decrement 385 | * @param Integer $value Value to decrement 386 | * 387 | * @return String 388 | */ 389 | function decrement($server, $port, $key, $value) 390 | { 391 | # Executing command : decrement 392 | if(($result = $this->exec('decr ' . $key . ' ' . $value, $server, $port))) 393 | { 394 | return $result; 395 | } 396 | return self::$_log; 397 | } 398 | 399 | /** 400 | * Flush all items on a server 401 | * Return the result 402 | * 403 | * @param String $server Hostname 404 | * @param Integer $port Hostname Port 405 | * @param Integer $delay Delay before flushing server 406 | * 407 | * @return String 408 | */ 409 | function flush_all($server, $port, $delay) 410 | { 411 | # Executing command : flush_all 412 | if(($result = $this->exec('flush_all ' . $delay, $server, $port))) 413 | { 414 | return $result; 415 | } 416 | return self::$_log; 417 | } 418 | 419 | /** 420 | * Search for item 421 | * Return all the items matching parameters if successful, false otherwise 422 | * 423 | * @param String $server Hostname 424 | * @param Integer $port Hostname Port 425 | * @param String $key Key to search 426 | * @param String $level Level of Detail 427 | * @param String $more More action 428 | * 429 | * @return array 430 | */ 431 | function search($server, $port, $search, $level = false, $more = false) 432 | { 433 | $slabs = array(); 434 | $items = false; 435 | 436 | # Executing command : stats 437 | if(($level == 'full') && ($result = $this->exec('stats', $server, $port))) 438 | { 439 | # Parsing result 440 | $result = $this->parse($result); 441 | $infinite = (isset($result['time'], $result['uptime'])) ? ($result['time'] - $result['uptime']) : 0; 442 | } 443 | 444 | # Executing command : slabs stats 445 | if(($result = $this->exec('stats slabs', $server, $port))) 446 | { 447 | # Parsing result 448 | $result = $this->parse($result); 449 | unset($result['active_slabs']); 450 | unset($result['total_malloced']); 451 | # Indexing by slabs 452 | foreach($result as $key => $value) 453 | { 454 | $key = preg_split('/:/', $key); 455 | $slabs[$key[0]] = true; 456 | } 457 | } 458 | 459 | # Exploring each slabs 460 | foreach($slabs as $slab => $unused) 461 | { 462 | # Executing command : stats cachedump 463 | if(($result = $this->exec('stats cachedump ' . $slab . ' 0', $server, $port))) 464 | { 465 | # Parsing result 466 | preg_match_all('/^ITEM ((?:.*)' . preg_quote($search, '/') . '(?:.*)) \[([0-9]*) b; ([0-9]*) s\]\r\n/imU', $result, $matchs, PREG_SET_ORDER); 467 | foreach($matchs as $item) 468 | { 469 | # Search & Delete 470 | if ($more == 'delete') { 471 | $items[] = $item[1] . ' : ' . $this->delete($server, $port, $item[1]); 472 | # Basic search 473 | } else { 474 | # Detail level 475 | if ($level == 'full') { 476 | $items[] = $item[1] . ' : [' . str_pad(Library_Data_Analysis::byteResize($item[2]), 7, ' ', STR_PAD_LEFT) . 'b, Expire : ' . (($item[3] == $infinite) ? '∞': Library_Data_Analysis::uptime($item[3] - time(), true)) . ']'; 477 | } else { 478 | $items[] = $item[1]; 479 | } 480 | } 481 | } 482 | } 483 | unset($slabs[$slab]); 484 | } 485 | 486 | if(is_array($items)) 487 | { 488 | sort($items); 489 | } 490 | 491 | return $items; 492 | } 493 | 494 | /** 495 | * Execute a telnet command on a server 496 | * Return the result 497 | * 498 | * @param String $server Hostname 499 | * @param Integer $port Hostname Port 500 | * @param String $command Command to execute 501 | * 502 | * @return String 503 | */ 504 | function telnet($server, $port, $command) 505 | { 506 | # Executing command 507 | if(($result = $this->exec($command, $server, $port))) 508 | { 509 | return $result; 510 | } 511 | return self::$_log; 512 | } 513 | } 514 | -------------------------------------------------------------------------------- /Library/Configuration/Loader.php: -------------------------------------------------------------------------------- 1 | 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 | /** 10 | * Configuration class for editing, saving, ... 11 | * 12 | * @since 19/05/2010 13 | */ 14 | class Library_Configuration_Loader 15 | { 16 | # Singleton 17 | protected static $_instance = null; 18 | 19 | # Configuration file 20 | protected static $_iniPath = './Config/Memcache.php'; 21 | 22 | # Configuration needed keys 23 | protected static $_iniKeys = array('stats_api', 24 | 'slabs_api', 25 | 'items_api', 26 | 'get_api', 27 | 'set_api', 28 | 'delete_api', 29 | 'flush_all_api', 30 | 'connection_timeout', 31 | 'max_item_dump', 32 | 'refresh_rate', 33 | 'memory_alert', 34 | 'hit_rate_alert', 35 | 'eviction_alert', 36 | 'file_path', 37 | 'servers'); 38 | 39 | protected static $_iniStdArray = array ( 40 | 'stats_api' => 'Server', 41 | 'slabs_api' => 'Server', 42 | 'items_api' => 'Server', 43 | 'get_api' => 'Server', 44 | 'set_api' => 'Server', 45 | 'delete_api' => 'Server', 46 | 'flush_all_api' => 'Server', 47 | 'connection_timeout' => '1', 48 | 'max_item_dump' => '100', 49 | 'refresh_rate' => 2, 50 | 'memory_alert' => '80', 51 | 'hit_rate_alert' => '90', 52 | 'eviction_alert' => '0', 53 | 'file_path' => 'Temp/', 54 | 'servers' => 55 | array ( 56 | 'Default' => 57 | array ( 58 | '127.0.0.1:11211' => 59 | array ( 60 | 'hostname' => '127.0.0.1', 61 | 'port' => '11211', 62 | ), 63 | ), 64 | ), 65 | ); 66 | 67 | # Storage 68 | protected static $_ini = array(); 69 | 70 | /** 71 | * Constructor, load configuration file and parse server list 72 | * 73 | * @return Void 74 | */ 75 | protected function __construct() 76 | { 77 | # Opening ini file 78 | if ( file_exists( self::$_iniPath ) ) { 79 | self::$_ini = require self::$_iniPath; 80 | } else { 81 | self::$_ini = self::$_iniStdArray; 82 | } 83 | } 84 | 85 | /** 86 | * Get Library_Configuration_Loader singleton 87 | * 88 | * @return Library_Configuration_Loader 89 | */ 90 | public static function singleton() 91 | { 92 | if(!isset(self::$_instance)) 93 | { 94 | self::$_instance = new self(); 95 | } 96 | return self::$_instance; 97 | } 98 | 99 | /** 100 | * Config key to retrieve 101 | * Return the value, or false if does not exists 102 | * 103 | * @param String $key Key to get 104 | * 105 | * @return Mixed 106 | */ 107 | public function get($key) 108 | { 109 | if(isset(self::$_ini[$key])) 110 | { 111 | return self::$_ini[$key]; 112 | } 113 | return false; 114 | } 115 | 116 | /** 117 | * Servers to retrieve from cluster 118 | * Return the value, or false if does not exists 119 | * 120 | * @param String $cluster Cluster to retreive 121 | * 122 | * @return Array 123 | */ 124 | public function cluster($cluster) 125 | { 126 | if(isset(self::$_ini['servers'][$cluster])) 127 | { 128 | return self::$_ini['servers'][$cluster]; 129 | } 130 | return array(); 131 | } 132 | 133 | /** 134 | * Check and return server data 135 | * Return the value, or false if does not exists 136 | * 137 | * @param String $server Server to retreive 138 | * 139 | * @return Array 140 | */ 141 | public function server($server) 142 | { 143 | foreach(self::$_ini['servers'] as $cluster => $servers) 144 | { 145 | if(isset(self::$_ini['servers'][$cluster][$server])) 146 | { 147 | return self::$_ini['servers'][$cluster][$server]; 148 | } 149 | } 150 | return array(); 151 | } 152 | 153 | /** 154 | * Config key to set 155 | * 156 | * @param String $key Key to set 157 | * @param Mixed $value Value to set 158 | * 159 | * @return Boolean 160 | */ 161 | public function set($key, $value) 162 | { 163 | self::$_ini[$key] = $value; 164 | } 165 | 166 | /** 167 | * Return actual ini file path 168 | * 169 | * @return String 170 | */ 171 | public function path() 172 | { 173 | return self::$_iniPath; 174 | } 175 | 176 | /** 177 | * Check if every ini keys are set 178 | * Return true if ini is correct, false otherwise 179 | * 180 | * @return Boolean 181 | */ 182 | public function check() 183 | { 184 | # Checking configuration keys 185 | foreach(self::$_iniKeys as $iniKey) 186 | { 187 | # Ini file key not set 188 | if(!isset(self::$_ini[$iniKey])) 189 | { 190 | return false; 191 | } 192 | } 193 | return true; 194 | } 195 | 196 | /** 197 | * Write ini file 198 | * Return true if written, false otherwise 199 | * 200 | * @return Boolean 201 | */ 202 | public function write() 203 | { 204 | if($this->check()) 205 | { 206 | return is_numeric(file_put_contents(self::$_iniPath, ' 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 | /** 10 | * Analysis of memcached command response 11 | * 12 | * @since 20/03/2010 13 | */ 14 | class Library_Data_Analysis 15 | { 16 | /** 17 | * Merge two arrays of stats from Command_XX::stats() 18 | * 19 | * @param Array $array Statistic from Command_XX::stats() 20 | * @param Array $stats Statistic from Command_XX::stats() 21 | * 22 | * @return Array 23 | */ 24 | public static function merge($array, $stats) 25 | { 26 | # Checking input 27 | if(!is_array($array)) 28 | { 29 | return $stats; 30 | } 31 | elseif(!is_array($stats)) 32 | { 33 | return $array; 34 | } 35 | 36 | # Merging Stats 37 | foreach($stats as $key => $value) 38 | { 39 | if(isset($array[$key]) && ($key != 'version') && ($key != 'uptime')) 40 | { 41 | $array[$key] += $value; 42 | } 43 | else 44 | { 45 | $array[$key] = $value; 46 | } 47 | } 48 | return $array; 49 | } 50 | 51 | /** 52 | * Diff two arrays of stats from Command_XX::stats() 53 | * 54 | * @param Array $array Statistic from Command_XX::stats() 55 | * @param Array $stats Statistic from Command_XX::stats() 56 | * 57 | * @return Array 58 | */ 59 | public static function diff($array, $stats) 60 | { 61 | # Checking input 62 | if(!is_array($array)) 63 | { 64 | return $stats; 65 | } 66 | elseif(!is_array($stats)) 67 | { 68 | return $array; 69 | } 70 | 71 | # Diff for each key 72 | foreach($stats as $key => $value) 73 | { 74 | if(isset($array[$key])) 75 | { 76 | $stats[$key] = $value - $array[$key]; 77 | } 78 | } 79 | 80 | return $stats; 81 | } 82 | 83 | /** 84 | * Analyse and return memcache stats command 85 | * 86 | * @param Array $stats Statistic from Command_XX::stats() 87 | * 88 | * @return Array 89 | */ 90 | public static function stats($stats) 91 | { 92 | if(!is_array($stats) || (count($stats) == 0)) 93 | { 94 | return false; 95 | } 96 | 97 | # Command set() 98 | $stats['set_rate'] = ($stats['cmd_set'] == 0) ? '0.0' : sprintf('%.1f', $stats['cmd_set'] / $stats['uptime'], 1); 99 | 100 | # Command get() 101 | $stats['get_hits_percent'] = ($stats['cmd_get'] == 0) ? ' - ' : sprintf('%.1f', $stats['get_hits'] / $stats['cmd_get'] * 100, 1); 102 | $stats['get_misses_percent'] = ($stats['cmd_get'] == 0) ? ' - ' : sprintf('%.1f', $stats['get_misses'] / $stats['cmd_get'] * 100, 1); 103 | $stats['get_rate'] = ($stats['cmd_get'] == 0) ? '0.0' : sprintf('%.1f', $stats['cmd_get'] / $stats['uptime'], 1); 104 | 105 | # Command delete(), version > 1.2.X 106 | if(isset($stats['delete_hits'], $stats['delete_misses'])) 107 | { 108 | $stats['cmd_delete'] = $stats['delete_hits'] + $stats['delete_misses']; 109 | $stats['delete_hits_percent'] = ($stats['cmd_delete'] == 0) ? ' - ' : sprintf('%.1f', $stats['delete_hits'] / $stats['cmd_delete'] * 100, 1); 110 | $stats['delete_misses_percent'] = ($stats['cmd_delete'] == 0) ? ' - ' : sprintf('%.1f', $stats['delete_misses'] / $stats['cmd_delete'] * 100, 1); 111 | } 112 | else 113 | { 114 | $stats['cmd_delete'] = 0; 115 | $stats['delete_hits_percent'] = ' - '; 116 | $stats['delete_misses_percent'] = ' - '; 117 | } 118 | $stats['delete_rate'] = ($stats['cmd_delete'] == 0) ? '0.0' : sprintf('%.1f', $stats['cmd_delete'] / $stats['uptime'], 1); 119 | 120 | # Command cas(), version > 1.2.X 121 | if(isset($stats['cas_hits'], $stats['cas_misses'], $stats['cas_badval'])) 122 | { 123 | $stats['cmd_cas'] = $stats['cas_hits'] + $stats['cas_misses'] + $stats['cas_badval']; 124 | $stats['cas_hits_percent'] = ($stats['cmd_cas'] == 0) ? ' - ' : sprintf('%.1f', $stats['cas_hits'] / $stats['cmd_cas'] * 100, 1); 125 | $stats['cas_misses_percent'] = ($stats['cmd_cas'] == 0) ? ' - ' : sprintf('%.1f', $stats['cas_misses'] / $stats['cmd_cas'] * 100, 1); 126 | $stats['cas_badval_percent'] = ($stats['cmd_cas'] == 0) ? ' - ' : sprintf('%.1f', $stats['cas_badval'] / $stats['cmd_cas'] * 100, 1); 127 | } 128 | else 129 | { 130 | $stats['cmd_cas'] = 0; 131 | $stats['cas_hits_percent'] = ' - '; 132 | $stats['cas_misses_percent'] = ' - '; 133 | $stats['cas_badval_percent'] = ' - '; 134 | } 135 | $stats['cas_rate'] = ($stats['cmd_cas'] == 0) ? '0.0' : sprintf('%.1f', $stats['cmd_cas'] / $stats['uptime'], 1); 136 | 137 | # Command increment(), version > 1.2.X 138 | if(isset($stats['incr_hits'], $stats['incr_misses'])) 139 | { 140 | $stats['cmd_incr'] = $stats['incr_hits'] + $stats['incr_misses']; 141 | $stats['incr_hits_percent'] = ($stats['cmd_incr'] == 0) ? ' - ' : sprintf('%.1f', $stats['incr_hits'] / $stats['cmd_incr'] * 100, 1); 142 | $stats['incr_misses_percent'] = ($stats['cmd_incr'] == 0) ? ' - ' : sprintf('%.1f', $stats['incr_misses'] / $stats['cmd_incr'] * 100, 1); 143 | } 144 | else 145 | { 146 | $stats['cmd_incr'] = 0; 147 | $stats['incr_hits_percent'] = ' - '; 148 | $stats['incr_misses_percent'] = ' - '; 149 | 150 | } 151 | $stats['incr_rate'] = ($stats['cmd_incr'] == 0) ? '0.0' : sprintf('%.1f', $stats['cmd_incr'] / $stats['uptime'], 1); 152 | 153 | # Command decrement(), version > 1.2.X 154 | if(isset($stats['decr_hits'], $stats['decr_misses'])) 155 | { 156 | $stats['cmd_decr'] = $stats['decr_hits'] + $stats['decr_misses']; 157 | $stats['decr_hits_percent'] = ($stats['cmd_decr'] == 0) ? ' - ' : sprintf('%.1f', $stats['decr_hits'] / $stats['cmd_decr'] * 100, 1); 158 | $stats['decr_misses_percent'] = ($stats['cmd_decr'] == 0) ? ' - ' : sprintf('%.1f', $stats['decr_misses'] / $stats['cmd_decr'] * 100, 1); 159 | } 160 | else 161 | { 162 | $stats['cmd_decr'] = 0; 163 | $stats['decr_hits_percent'] = ' - '; 164 | $stats['decr_misses_percent'] = ' - '; 165 | } 166 | $stats['decr_rate'] = ($stats['cmd_decr'] == 0) ? '0.0' : sprintf('%.1f', $stats['cmd_decr'] / $stats['uptime'], 1); 167 | 168 | # Command decrement(), version > 1.4.7 169 | if(isset($stats['touch_hits'], $stats['touch_misses'])) 170 | { 171 | $stats['cmd_touch'] = $stats['touch_hits'] + $stats['touch_misses']; 172 | $stats['touch_hits_percent'] = ($stats['cmd_touch'] == 0) ? ' - ' : sprintf('%.1f', $stats['touch_hits'] / $stats['cmd_touch'] * 100, 1); 173 | $stats['touch_misses_percent'] = ($stats['cmd_touch'] == 0) ? ' - ' : sprintf('%.1f', $stats['touch_misses'] / $stats['cmd_touch'] * 100, 1); 174 | } 175 | else 176 | { 177 | $stats['cmd_touch'] = 0; 178 | $stats['touch_hits_percent'] = ' - '; 179 | $stats['touch_misses_percent'] = ' - '; 180 | } 181 | $stats['touch_rate'] = ($stats['cmd_touch'] == 0) ? '0.0' : sprintf('%.1f', $stats['cmd_touch'] / $stats['uptime'], 1); 182 | 183 | # Total hit & miss 184 | #$stats['cmd_total'] = $stats['cmd_get'] + $stats['cmd_set'] + $stats['cmd_delete'] + $stats['cmd_cas'] + $stats['cmd_incr'] + $stats['cmd_decr']; 185 | #$stats['hit_percent'] = ($stats['cmd_get'] == 0) ? '0.0' : sprintf('%.1f', ($stats['get_hits']) / ($stats['get_hits'] + $stats['get_misses']) * 100, 1); 186 | #$stats['miss_percent'] = ($stats['cmd_get'] == 0) ? '0.0' : sprintf('%.1f', ($stats['get_misses']) / ($stats['get_hits'] + $stats['get_misses']) * 100, 1); 187 | 188 | # Command flush_all 189 | if(isset($stats['cmd_flush'])) 190 | { 191 | $stats['flush_rate'] = ($stats['cmd_flush'] == 0) ? '0.0' : sprintf('%.1f', $stats['cmd_flush'] / $stats['uptime'], 1); 192 | } 193 | else 194 | { 195 | $stats['flush_rate'] = '0.0'; 196 | } 197 | 198 | # Cache size 199 | $stats['bytes_percent'] = ($stats['limit_maxbytes'] == 0) ? '0.0' : sprintf('%.1f', $stats['bytes'] / $stats['limit_maxbytes'] * 100, 1); 200 | 201 | # Request rate 202 | $stats['request_rate'] = sprintf('%.1f', ($stats['cmd_get'] + $stats['cmd_set'] + $stats['cmd_delete'] + $stats['cmd_cas'] + $stats['cmd_incr'] + $stats['cmd_decr']) / $stats['uptime'], 1); 203 | $stats['hit_rate'] = sprintf('%.1f', ($stats['get_hits']) / $stats['uptime'], 1); 204 | $stats['miss_rate'] = sprintf('%.1f', ($stats['get_misses']) / $stats['uptime'], 1); 205 | 206 | # Eviction & reclaimed rate 207 | $stats['eviction_rate'] = ($stats['evictions'] == 0) ? '0.0' : sprintf('%.1f', $stats['evictions'] / $stats['uptime'], 1); 208 | $stats['reclaimed_rate'] = (!isset($stats['reclaimed']) || ($stats['reclaimed'] == 0)) ? '0.0' : sprintf('%.1f', $stats['reclaimed'] / $stats['uptime'], 1); 209 | 210 | return $stats; 211 | } 212 | 213 | /** 214 | * Analyse and return memcache slabs command 215 | * 216 | * @param Array $slabs Statistic from Command_XX::slabs() 217 | * 218 | * @return Array 219 | */ 220 | public static function slabs($slabs) 221 | { 222 | # Initializing Used Slabs 223 | $slabs['used_slabs'] = 0; 224 | $slabs['total_wasted'] = 0; 225 | 226 | # Request Rate par Slabs 227 | foreach($slabs as $id => $slab) 228 | { 229 | # Check if it's a Slab 230 | if(is_numeric($id)) 231 | { 232 | # Check if Slab is used 233 | if($slab['used_chunks'] > 0) 234 | { 235 | $slabs['used_slabs']++; 236 | } 237 | $slabs[$id]['request_rate'] = sprintf('%.1f', ($slab['get_hits'] + $slab['cmd_set'] + $slab['delete_hits'] + $slab['cas_hits'] + $slab['cas_badval'] + $slab['incr_hits'] + $slab['decr_hits']) / $slabs['uptime'], 1); 238 | $requested = isset($slab['items:mem_requested']) // Post Memcached 1.5.17 239 | ? $slab['items:mem_requested'] 240 | : (isset($slab['mem_requested']) ? $slab['mem_requested'] : 0); 241 | $slabs[$id]['mem_wasted'] = (($slab['total_chunks'] * $slab['chunk_size']) < $requested) 242 | ? (($slab['total_chunks'] - $slab['used_chunks']) * $slab['chunk_size']) 243 | : (($slab['total_chunks'] * $slab['chunk_size']) - $requested); 244 | $slabs['total_wasted'] += $slabs[$id]['mem_wasted']; 245 | } 246 | } 247 | 248 | # Checking server total malloced > 0 249 | if(!isset($slabs['total_malloced'])) 250 | { 251 | $slabs['total_malloced'] = 0; 252 | } 253 | 254 | return $slabs; 255 | } 256 | 257 | /** 258 | * Calculate Uptime 259 | * 260 | * @param Integer $uptime Uptime timestamp 261 | * @param Boolean $compact Compact Mode 262 | * 263 | * @return String 264 | */ 265 | public static function uptime($uptime, $compact = false) 266 | { 267 | if($uptime > 0) 268 | { 269 | $days = floor($uptime/60/60/24); 270 | $hours = $uptime/60/60%24; 271 | $mins = $uptime/60%60; 272 | if(($days + $hours + $mins) == 0) 273 | { 274 | return ' less than 1 min'; 275 | } 276 | if ($compact == false) { 277 | return $days . ' day' . (($days > 1) ? 's' : '') . ' ' . $hours . ' hr' . (($hours > 1) ? 's' : '') . ' ' . $mins . ' min' . (($mins > 1) ? 's' : ''); 278 | } else { 279 | return $days . 'd ' . $hours . 'h ' . $mins . 'm'; 280 | } 281 | } 282 | return ' - '; 283 | } 284 | 285 | /** 286 | * Resize a byte value 287 | * 288 | * @param Integer $value Value to resize 289 | * 290 | * @return String 291 | */ 292 | public static function byteResize($value) 293 | { 294 | # Unit list 295 | $units = array('', 'K', 'M', 'G', 'T'); 296 | 297 | # Resizing 298 | foreach($units as $unit) 299 | { 300 | if($value < 1024) 301 | { 302 | break; 303 | } 304 | $value /= 1024; 305 | } 306 | return sprintf('%.1f %s', $value, $unit); 307 | } 308 | 309 | /** 310 | * Resize a value 311 | * 312 | * @param Integer $value Value to resize 313 | * 314 | * @return String 315 | */ 316 | public static function valueResize($value) 317 | { 318 | # Unit list 319 | $units = array('', 'K', 'M', 'G', 'T'); 320 | 321 | # Resizing 322 | foreach($units as $unit) 323 | { 324 | if($value < 1000) 325 | { 326 | break; 327 | } 328 | $value /= 1000; 329 | } 330 | return sprintf('%.1f%s', $value, $unit); 331 | } 332 | 333 | /** 334 | * Resize a hit value 335 | * 336 | * @param Integer $value Hit value to resize 337 | * 338 | * @return String 339 | */ 340 | public static function hitResize($value) 341 | { 342 | # Unit list 343 | $units = array('', 'K', 'M', 'G', 'T'); 344 | 345 | # Resizing 346 | foreach($units as $unit) 347 | { 348 | if($value < 10000000) 349 | { 350 | break; 351 | } 352 | $value /= 1000; 353 | } 354 | return sprintf('%.0f%s', $value, $unit); 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /Library/Data/Error.php: -------------------------------------------------------------------------------- 1 | 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 | /** 10 | * Error container 11 | * 12 | * @since 11/10/2010 13 | */ 14 | class Library_Data_Error 15 | { 16 | private static $_errors = array(); 17 | 18 | /** 19 | * Add an error to the container 20 | * Return true if successful, false otherwise 21 | * 22 | * @param String $error Error message 23 | * 24 | * @return Boolean 25 | */ 26 | public static function add($error) 27 | { 28 | return array_push(self::$_errors, $error); 29 | } 30 | 31 | /** 32 | * Return last Error message 33 | * 34 | * @return Mixed 35 | */ 36 | public static function last() 37 | { 38 | return (isset(self::$_errors[count(self::$_errors) - 1])) ? self::$_errors[count(self::$_errors) - 1] : null; 39 | } 40 | 41 | /** 42 | * Return errors count 43 | * 44 | * @return Integer 45 | */ 46 | public static function count() 47 | { 48 | return count(self::$_errors); 49 | } 50 | } -------------------------------------------------------------------------------- /Library/Data/Version.php: -------------------------------------------------------------------------------- 1 | 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 | /** 10 | * Version container 11 | * 12 | * @since 24/08/2011 13 | */ 14 | class Library_Data_Version 15 | { 16 | # Version file 17 | protected static $_file = 'latest'; 18 | 19 | # Google Code latest version data file 20 | protected static $_latest = ''; 21 | 22 | # Time between HTTP check 23 | protected static $_time = 1296000; # 15 days 24 | 25 | /** 26 | * Check for the latest version, from local cache or via http 27 | * Return true if a newer version is available, false otherwise 28 | * 29 | * @return Boolean 30 | */ 31 | public static function check() 32 | { 33 | # Loading ini file 34 | $_ini = Library_Configuration_Loader::singleton(); 35 | 36 | # Version definition file path 37 | $path = rtrim($_ini->get('file_path'), '/') . DIRECTORY_SEPARATOR . self::$_file; 38 | 39 | # Checking if file was modified for less than 15 days ago 40 | if((is_array($stats = @stat($path))) && (isset($stats['mtime'])) && ($stats['mtime'] > (time() - self::$_time))) 41 | { 42 | # Opening file and checking for latest version 43 | return (version_compare(CURRENT_VERSION, file_get_contents($path)) == -1); 44 | } 45 | else 46 | { 47 | # Getting last version 48 | /** 49 | * @todo Get latest tag/release via GitHub API 50 | */ 51 | if($latest = @file_get_contents(self::$_latest)) 52 | { 53 | # Saving latest version in file 54 | file_put_contents($path, $latest); 55 | 56 | # Checking for latest version 57 | return (version_compare(CURRENT_VERSION, $latest) == -1); 58 | } else { 59 | # To avoid error spam 60 | file_put_contents($path, 'Net unreachable'); 61 | return true; 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Library/HTML/Components.php: -------------------------------------------------------------------------------- 1 | 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 | /** 10 | * Manipulation of HTML 11 | * 12 | * @since 05/04/2010 13 | */ 14 | class Library_HTML_Components 15 | { 16 | /** 17 | * Dump server list in an HTML select 18 | * 19 | * @return String 20 | */ 21 | public static function serverSelect($name, $selected = '', $class = '', $events = '') 22 | { 23 | # Loading ini file 24 | $_ini = Library_Configuration_Loader::singleton(); 25 | 26 | # Select Name 27 | $serverList = ''; 51 | } 52 | 53 | /** 54 | * Dump cluster list in an HTML select 55 | * 56 | * @return String 57 | */ 58 | public static function clusterSelect($name, $selected = '', $class = '', $events = '') 59 | { 60 | # Loading ini file 61 | $_ini = Library_Configuration_Loader::singleton(); 62 | 63 | # Select Name 64 | $clusterList = ''; 80 | } 81 | 82 | /** 83 | * Dump server response in proper formatting 84 | * 85 | * @param String $hostname Hostname 86 | * @param String $port Port 87 | * @param Mixed $data Data (reponse) 88 | * 89 | * @return String 90 | */ 91 | public static function serverResponse($hostname, $port, $data) 92 | { 93 | $header = 'Server ' . $hostname . ':' . $port . "\r\n"; 94 | $return = ''; 95 | if(is_array($data)) 96 | { 97 | foreach($data as $string) 98 | { 99 | $return .= $string . "\r\n"; 100 | } 101 | return $header . htmlentities($return, ENT_NOQUOTES | 0, 'UTF-8') . "\r\n"; 102 | } 103 | return $header . $return . $data . "\r\n"; 104 | } 105 | 106 | /** 107 | * Dump api list un HTML select with select name 108 | * 109 | * @param String $iniAPI API Name from ini file 110 | * @param String $id Select ID 111 | * 112 | * @return String 113 | */ 114 | public static function apiList($iniAPI = '', $id) 115 | { 116 | return ''; 121 | } 122 | 123 | /** 124 | * Used to see if an option is selected 125 | * 126 | * @param String $actual Actual value 127 | * @param String $selected Selected value 128 | * 129 | * @return String 130 | */ 131 | private static function selected($actual, $selected) 132 | { 133 | if($actual == $selected) 134 | { 135 | return 'selected="selected"'; 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /Library/Loader.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015-2017 WP-Cloud 5 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 6 | * @package phpMemcachedAdmin 7 | */ 8 | 9 | # Constants declaration 10 | define('CURRENT_VERSION', '1.3.0-dev'); 11 | 12 | # PHP < 5.3 Compatibility 13 | if ( ! defined('ENT_IGNORE')) { 14 | define('ENT_IGNORE', 0); 15 | } 16 | 17 | # Autoloader 18 | spl_autoload_register(function ($class) { 19 | require_once str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; 20 | }); 21 | -------------------------------------------------------------------------------- /Public/Scripts/Highcharts/standalone-framework.js: -------------------------------------------------------------------------------- 1 | /* 2 | Highcharts JS v3.0.5 (2013-08-23) 3 | 4 | Standalone Highcharts Framework 5 | 6 | License: MIT License 7 | */ 8 | var HighchartsAdapter=function(){function n(c){function a(a,b,d){a.removeEventListener(b,d,!1)}function d(a,b,d){d=a.HCProxiedMethods[d.toString()];a.detachEvent("on"+b,d)}function b(b,c){var g=b.HCEvents,i,h,l,f;if(b.removeEventListener)i=a;else if(b.attachEvent)i=d;else return;c?(h={},h[c]=!0):h=g;for(f in h)if(g[f])for(l=g[f].length;l--;)i(b,f,g[f][l])}c.HCExtended||Highcharts.extend(c,{HCExtended:!0,HCEvents:{},bind:function(b,a){var d=this,c=this.HCEvents,h;if(d.addEventListener)d.addEventListener(b, 9 | a,!1);else if(d.attachEvent){h=function(b){a.call(d,b)};if(!d.HCProxiedMethods)d.HCProxiedMethods={};d.HCProxiedMethods[a.toString()]=h;d.attachEvent("on"+b,h)}c[b]===q&&(c[b]=[]);c[b].push(a)},unbind:function(c,k){var g,i;c?(g=this.HCEvents[c]||[],k?(i=HighchartsAdapter.inArray(k,g),i>-1&&(g.splice(i,1),this.HCEvents[c]=g),this.removeEventListener?a(this,c,k):this.attachEvent&&d(this,c,k)):(b(this,c),this.HCEvents[c]=[])):(b(this),this.HCEvents={})},trigger:function(b,a){var d=this.HCEvents[b]|| 10 | [],c=d.length,h,f;for(h=function(){a.defaultPrevented=!0};c--;){f=d[c];if(a.stopped)break;a.preventDefault=h;a.target=this;f.call(this,a)===!1&&a.preventDefault()}}});return c}var q,f=document,o=[],j=[],p,m;Math.easeInOutSine=function(c,a,d,b){return-d/2*(Math.cos(Math.PI*c/b)-1)+a};return{init:function(c){if(!f.defaultView)this._getStyle=function(a,d){var b;return a.style[d]?a.style[d]:(d==="opacity"&&(d="filter"),b=a.currentStyle[d.replace(/\-(\w)/g,function(a,b){return b.toUpperCase()})],d==="filter"&& 11 | (b=b.replace(/alpha\(opacity=([0-9]+)\)/,function(b,a){return a/100})),b===""?1:b)},this.adapterRun=function(a,d){var b={width:"clientWidth",height:"clientHeight"}[d];if(b)return a.style.zoom=1,a[b]-2*parseInt(HighchartsAdapter._getStyle(a,"padding"),10)};if(!Array.prototype.forEach)this.each=function(a,d){for(var b=0,c=a.length;b=b.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();a=this.options.curAnim[this.prop]=!0;for(e in b.curAnim)b.curAnim[e]!== 14 | !0&&(a=!1);a&&b.complete&&b.complete.call(this.elem);b=!1}else e=c-this.startTime,this.state=e/b.duration,this.pos=b.easing(e,0,1,b.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update(),b=!0;return b}};this.animate=function(a,d,b){var e,f="",g,i,h;a.stopAnimation=!1;if(typeof b!=="object"||b===null)e=arguments,b={duration:e[2],easing:e[3],complete:e[4]};if(typeof b.duration!=="number")b.duration=400;b.easing=Math[b.easing]||Math.easeInOutSine;b.curAnim=Highcharts.extend({},d); 15 | for(h in d)i=new m(a,b,h),g=null,h==="d"?(i.paths=c.init(a,a.d,d.d),i.toD=d.d,e=0,g=1):a.attr?e=a.attr(h):(e=parseFloat(this._getStyle(a,h))||0,h!=="opacity"&&(f="px")),g||(g=parseFloat(d[h])),i.custom(e,g,f)}},_getStyle:function(c,a){return window.getComputedStyle(c).getPropertyValue(a)},getScript:function(c,a){var d=f.getElementsByTagName("head")[0],b=f.createElement("script");b.type="text/javascript";b.src=c;b.onload=a;d.appendChild(b)},inArray:function(c,a){return a.indexOf?a.indexOf(c):o.indexOf.call(a, 16 | c)},adapterRun:function(c,a){return parseInt(HighchartsAdapter._getStyle(c,a),10)},grep:function(c,a){return o.filter.call(c,a)},map:function(c,a){for(var d=[],b=0,e=c.length;b' 199 | + '
' 200 | + '
Name (Optional)
' 201 | + '
IP/Hostname
' 202 | + '
Port
' 203 | + '
'; 207 | clusterDiv.setAttribute('id', 'cluster_' + cluster_id); 208 | document.getElementById('server_form').appendChild(clusterDiv); 209 | addServer(cluster_id); 210 | } 211 | function addServer(current_cluster_id) { 212 | var serverDiv = document.createElement('div'); 213 | server_id++; 214 | serverDiv.innerHTML = '
' 215 | + ' ' 217 | + ' ' 220 | + ' ' 222 | + 'Delete' + '
'; 223 | serverDiv.setAttribute('id', 'server_' + server_id); 224 | document.getElementById('cluster_' + current_cluster_id).insertBefore(serverDiv, document.getElementById('cluster_' + current_cluster_id + '_commands')); 225 | } 226 | function deleteServerOrCluster(divID) { 227 | var div = document.getElementById(divID); 228 | div.parentNode.removeChild(div); 229 | } 230 | function nameOnChange(target) { 231 | portObject = document.getElementById('port_' + target); 232 | portObject.setAttribute("onchange", "return false;"); 233 | hostObject = document.getElementById('host_' + target); 234 | hostObject.setAttribute("onchange", "return false;"); 235 | } 236 | function hostOnFocus(object) { 237 | if (object.value == 'hostname') { 238 | object.value = ''; 239 | } 240 | } 241 | function hostOnBlur(object) { 242 | if (object.value == '') { 243 | object.value = 'hostname'; 244 | } 245 | } 246 | function hostOnChange(target) { 247 | document.getElementById(target); 248 | if (object.value == '') { 249 | object.value = 'port'; 250 | } 251 | } 252 | function portOnFocus(object) { 253 | if (object.value == 'port') { 254 | object.value = ''; 255 | } 256 | } 257 | function portOnBlur(object) { 258 | if (object.value == '') { 259 | object.value = 'port'; 260 | } 261 | } 262 | function hostOrPortOnChange(target) { 263 | 264 | nameObject = document.getElementById('name_' + target); 265 | hostObject = document.getElementById('host_' + target); 266 | portObject = document.getElementById('port_' + target); 267 | if ((nameObject.value == '') || ((nameObject.value != hostObject.value + ':' + portObject.value))) { 268 | nameObject.value = hostObject.value + ':' + portObject.value; 269 | } 270 | } 271 | function ajax(url, target) { 272 | if (window.XMLHttpRequest) { 273 | req = new XMLHttpRequest(); 274 | req.onreadystatechange = function() { 275 | ajaxDone(target); 276 | }; 277 | req.open("GET", url, true); 278 | req.send(null); 279 | } else if (window.ActiveXObject) { 280 | req = new ActiveXObject('Microsoft.XMLHTTP'); 281 | if (req) { 282 | req.onreadystatechange = function() { 283 | ajaxDone(target); 284 | }; 285 | req.open("GET", url, true); 286 | req.send(); 287 | } 288 | } 289 | setTimeout("ajax(page, 'stats')", timeout); 290 | } 291 | function ajaxDone(target) { 292 | if (req.readyState == 4) { 293 | if (req.status == 200 || req.status == 304) { 294 | results = req.responseText; 295 | document.getElementById(target).innerHTML = results; 296 | } else { 297 | document.getElementById(target).innerHTML = "Loading stats error : " 298 | + req.statusText; 299 | } 300 | } 301 | } -------------------------------------------------------------------------------- /Public/Styles/Style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #FEFEFE; 3 | font-family: Verdana,Tahoma,"Segoe UI", Arial; 4 | font-size: 0.8em; 5 | margin-top: 10px; 6 | } 7 | 8 | a { 9 | color: #EEE; 10 | text-decoration: none; 11 | cursor: pointer; 12 | } 13 | 14 | a:hover { 15 | color: #A00; 16 | } 17 | 18 | input,select,textarea { 19 | -moz-border-radius: 2px; 20 | -webkit-border-radius: 2px; 21 | border-radius: 2px; 22 | border-width: 1px; 23 | border-style: solid; 24 | border-color: #AAA; 25 | width: 298px; 26 | font-family: Tahoma; 27 | font-size: 1em; 28 | } 29 | 30 | textarea { 31 | width: 494px; 32 | resize: none; 33 | } 34 | 35 | select { 36 | width: 300px; 37 | } 38 | 39 | input:focus,textarea:focus { 40 | border-width: 1px; 41 | border-style: solid; 42 | border-color: #EEE; 43 | } 44 | 45 | input:hover { 46 | color: #A00; 47 | } 48 | input[type=submit] { 49 | cursor: pointer; 50 | } 51 | 52 | img { 53 | border: none; 54 | } /** hr */ 55 | hr { 56 | height: 0; 57 | border: none; 58 | border-bottom-width: 1px; 59 | border-bottom-style: solid; 60 | border-bottom-color: #EEE; 61 | } 62 | 63 | .menu { 64 | border-width: 1px; 65 | border-style: solid; 66 | border-color: #a0312a; 67 | color: #eee; 68 | width: 198px; 69 | } 70 | 71 | .item { 72 | font-family: "Bitstream Vera Sans Mono", 73 | "Lucida Sans Typewriter", 74 | "DejaVu Sans Mono", 75 | Consolas, 76 | "Andale Mono", 77 | "Lucida Console", 78 | "Liberation Mono", 79 | "Nimbus Mono L", 80 | Monaco, 81 | "Courier New", 82 | Courier, 83 | monospace; 84 | cursor: hand; 85 | } 86 | 87 | .loading { 88 | text-decoration: blink; 89 | visibility: hidden; 90 | } 91 | 92 | .full-size { 93 | width: 980px; 94 | } 95 | 96 | .size-0 { 97 | width: 494px; 98 | } 99 | 100 | .size-1 { 101 | width: 696px; 102 | } 103 | 104 | .size-2 { 105 | width: 398px; 106 | } 107 | 108 | .size-4 { 109 | width: 290px; 110 | } 111 | 112 | .size-5 { 113 | width: 226px; 114 | } 115 | 116 | .padding { 117 | padding: 3px 7px 3px 7px; 118 | } 119 | 120 | .corner { 121 | -moz-border-radius: 2px; 122 | -webkit-border-radius: 2px; 123 | border-radius: 2px; 124 | } 125 | 126 | .header { 127 | border-width: 1px; 128 | border-style: solid; 129 | border-color: #9c3c36; 130 | background: #B5463F; 131 | font-weight: bold; 132 | color: #fff; 133 | clear: both 134 | } 135 | 136 | .sub-header { 137 | border-width: 1px; 138 | border-style: solid; 139 | border-color: #514845; 140 | background: #635855; 141 | font-weight: bold; 142 | color: #fff; 143 | clear: both; 144 | margin-top: 10px 145 | } 146 | 147 | .container { 148 | border-width: 1px; 149 | border-style: solid; 150 | border-color: #d0d0d0; 151 | background: #ebebeb; 152 | font-weight: inherit; 153 | color: #000; 154 | margin-top: 1px; 155 | clear: both 156 | } 157 | 158 | .list { 159 | border-width: 1px; 160 | border-style: solid; 161 | border-color: #9c3c36; 162 | background: #B5463F; 163 | font-weight: bold; 164 | color: #fff 165 | } 166 | 167 | .button { 168 | padding: 1px 20px; 169 | -moz-border-radius: 2px; 170 | -webkit-border-radius: 2px; 171 | border-radius: 2px 172 | } 173 | 174 | .live { 175 | font-family: "Bitstream Vera Sans Mono", 176 | "Lucida Sans Typewriter", 177 | "DejaVu Sans Mono", 178 | Consolas, 179 | "Andale Mono", 180 | "Lucida Console", 181 | "Liberation Mono", 182 | "Nimbus Mono L", 183 | Monaco, 184 | "Courier New", 185 | Courier, 186 | monospace; 187 | font-size: 12px; 188 | overflow: visible; 189 | white-space: pre-wrap 190 | } 191 | 192 | .line { 193 | min-height: 18px; 194 | padding-top: 3px; 195 | padding-bottom: 2px; 196 | clear: both 197 | } 198 | 199 | .left { 200 | float: left; 201 | min-width: 126px; 202 | font-weight: bold 203 | } 204 | .right { 205 | float: right 206 | } 207 | .setting { 208 | min-width: 180px 209 | } 210 | 211 | .slabs { 212 | min-width: 104px 213 | } 214 | .help { 215 | cursor:help; 216 | } 217 | 218 | .container hr { 219 | height: 0; 220 | border: none; 221 | border-bottom-width: 1px; 222 | border-bottom-style: solid; 223 | border-bottom-color: #fff; 224 | } 225 | 226 | .grey { 227 | color: #EEE 228 | } 229 | 230 | .green { 231 | color: #40aaba 232 | } 233 | 234 | .red { 235 | background: #b5463f; 236 | color: #fff; 237 | font-weight: bold 238 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # phpMemcachedAdmin 2 | > Graphic stand-alone administration for memcached to monitor and debug purpose 3 | 4 | [![Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-green.svg)](http://www.apache.org/licenses/LICENSE-2.0) 5 | [![Packagist](https://img.shields.io/packagist/v/wp-cloud/phpmemcacheadmin.svg)](https://packagist.org/packages/wp-cloud/phpmemcacheadmin) 6 | [![GitHub tag](https://img.shields.io/github/tag/wp-cloud/phpmemcacheadmin.svg)](https://github.com/wp-cloud/phpmemcacheadmin/releases) 7 | 8 | ## Development 9 | 10 | ### Coding Style 11 | The project follows the [PSR-2 Coding Style](http://www.php-fig.org/psr/psr-2/) but as of release 1.2.3 the code base needs a overall style fix. 12 | 13 | ### Contribution 14 | All contributions are welcome (Pull Requests, Report Bugs/Issues). 15 | 16 | `Please send us your fixes or improvements whenever possible!` 17 | 18 | ## License: _[Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0)_ 19 | 20 | phpMemcachedAdmin 21 | Copyright (C) 2010-2015 Cyrille Mahieux 22 | Copyright (C) 2015-2017 WP-Cloud 23 | 24 | Licensed under the Apache License, Version 2.0 (the "License"); 25 | you may not use this file except in compliance with the License. 26 | You may obtain a copy of the License at 27 | 28 | http://www.apache.org/licenses/LICENSE-2.0 29 | 30 | Unless required by applicable law or agreed to in writing, software 31 | distributed under the License is distributed on an "AS IS" BASIS, 32 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | See the License for the specific language governing permissions and limitations 34 | under the License. 35 | -------------------------------------------------------------------------------- /Temp/.version: -------------------------------------------------------------------------------- 1 | Net unreachable -------------------------------------------------------------------------------- /View/Commands/Commands.phtml: -------------------------------------------------------------------------------- 1 |
2 |
Console
3 |
4 |

  5 |         
6 |
7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
Execute predefined Command
15 |
16 | 17 | 18 | 87 | 98 | 99 |
19 |
20 |
21 | Execute command on one or all memcached servers 22 |
23 |
24 |
25 | Command 26 | 27 | 36 | 37 |
38 | 44 | 50 | 56 | 62 | 68 |
69 | Server 70 | 71 | 72 | 73 |
74 |
75 | API 76 | 77 | get('get_api'), 'request_api'); ?> 78 | 79 |
80 |
81 |
82 | 84 |
85 |
86 |
88 | Available commands : 89 |
    90 |
  • get : retreive a key value
  • 91 |
  • set : set a key/value pair
  • 92 |
  • delete : delete a specific key
  • 93 |
  • increment : increment a numeric key value
  • 94 |
  • decrement : decrement a numeric key value
  • 95 |
  • flush all : flush a Memcached server
  • 96 |
97 |
100 |
101 | 102 |
Execute Telnet Commands
103 |
104 | 105 | 106 | 126 | 131 | 132 |
107 |
108 | Execute telnet command on one or all memcached servers 109 |
110 |
111 |
112 | 113 |
114 |
115 | Server 116 | 117 | 118 | 119 |
120 |
121 |
122 | 124 |
125 |
127 |

You can use this thing to execute any telnet command to any memcached server

128 |

It will connect to the server, execute the command and return it in the console

129 |

For more informations about memcached commands, see memcached protocol here

130 |
133 |
134 | 135 |
Search Key
136 |
137 | 138 | 139 | 180 | 186 | 187 |
140 |
141 | Search for a key on one or all memcached servers 142 |
143 |
144 |
145 | Key 146 | 147 | 148 | 149 |
150 |
151 | Server 152 | 153 | 154 | 155 |
156 |
157 | Detail Level 158 | 159 | 163 | 164 |
165 |
166 | Action 167 | 168 | 172 | 173 |
174 |
175 |
176 | 178 |
179 |
181 | Warning ! 182 |

This thing is only for debuging issue, do not use it in a production environment as it can lock or impact your memcached servers performances.

183 |

Also keep in mind that it does not list all keys. It lists keys up to a certain buffer size (1 or 2MB), and it list key that are expired.

184 |

You can also use a PCRE regular expression

185 |
188 |
189 |
-------------------------------------------------------------------------------- /View/Configure/Configure.phtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
Commands Configuration
5 |
6 |
7 |
8 |

Memcached commands API used by phpMemcacheAdmin

9 |

PECL Memcache was found on this server

10 |

PECL Memcached was found on this server

11 |
12 |
13 |
14 | Stats 15 | get('stats_api'), 'stats_api'); ?> 16 |
17 |
18 | Slabs 19 | get('slabs_api'), 'slabs_api'); ?> 20 |
21 |
22 | Items 23 | get('items_api'), 'items_api'); ?> 24 |
25 |
26 | Get 27 | get('get_api'), 'get_api'); ?> 28 |
29 |
30 | Set 31 | get('set_api'), 'set_api'); ?> 32 |
33 |
34 | Delete 35 | get('delete_api'), 'delete_api'); ?> 36 |
37 |
38 | Flush All 39 | get('flush_all_api'), 'flush_all_api'); ?> 40 |
41 |
42 |
43 | 44 |
45 |
46 |
47 | 48 |
Live Stats Configuration
49 |
50 |
51 |
52 | Alert & refresh rate for Live Stats 53 |
54 |
55 |
56 | Refresh Rate in sec 57 | 58 |
59 |
60 | Memory Alert 61 | 62 |
63 |
64 | Hit Rate Alert 65 | 66 |
67 |
68 | Eviction Alert 69 | 70 |
71 |
72 | Temp Path 73 | 74 |
75 |
76 |
77 | 78 |
79 |
80 |
81 | 82 |
Miscellaneous Configuration
83 |
84 |
85 |
86 | Server connection timeout & miscellaneous 87 |
88 |
89 |
90 | Timeout in sec 91 | 92 |
93 |
94 | Max Items 95 | 96 |
97 |
98 |
99 | 100 |
101 |
102 |
103 | 104 |
105 |
106 |
Server List
107 |
108 |
109 |
110 |

Servers list used by phpMemcacheAdmin

111 |

The server name will be filled by default with hostname:port

112 |
113 |
114 | get('servers') as $cluster => $servers) 121 | { 122 | $cluster_id++; ?> 123 |
124 |
125 | Cluster 126 |
127 |
Name (Optional)
128 |
IP/Hostname
129 |
Port
130 |
131 | $server) 133 | { 134 | $server_id++; ?> 135 |
136 |
137 | 141 | 146 | onchange="hostOrPortOnChange()" 147 | onKeyUp="hostOrPortOnChange()" 148 | 150 | onfocus="hostOnFocus(this)" 151 | onblur="hostOnBlur(this)"/> 152 | 157 | onchange="hostOrPortOnChange()" 158 | onKeyUp="hostOrPortOnChange()" 159 | 161 | onfocus="portOnFocus(this)" 162 | onblur="portOnBlur(this)"/> 163 | Delete 164 |
165 |
166 | 167 | 172 |
173 | 174 |
175 |
176 |
177 | Add New Cluster 178 | 179 |
180 |
181 |
182 | 186 | 187 |
188 |
189 | For more information about configuring phpMemcachedAdmin, see installation guide 190 | here 191 |
192 |
193 |
194 |
195 | -------------------------------------------------------------------------------- /View/Footer.phtml: -------------------------------------------------------------------------------- 1 | 5 |
6 | A newer version of phpMemcachedAdmin may be available , visit the GitHub repo to know more. 7 | 9 | 15 |
16 | 17 | -------------------------------------------------------------------------------- /View/Header.phtml: -------------------------------------------------------------------------------- 1 | '; ?> 2 | 3 | 4 | 5 | phpMemcachedAdmin <?php echo CURRENT_VERSION; ?> 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
phpMemcachedAdmin 14 |
15 |
16 | 20 | Live Stats | 21 | 23 | See Live Stats | 24 | 29 | Actually seeing 30 | 32 | See Stats for 33 | 37 | | 38 | 42 | Executing Commands on Servers 43 | 45 | Execute Commands on Servers 46 | 48 | | 49 | 53 | Editing Configuration 54 | 56 | Edit Configuration 57 | 59 |
60 | 61 | 66 | -------------------------------------------------------------------------------- /View/LiveStats/Frame.phtml: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 |
Live Stats
9 | $_ini->get('refresh_rate')) 12 | { ?> 13 |
14 | Connections errors were discovered, to prevent any problem, refresh rate was increased by 15 | get('refresh_rate')); ?> seconds. 16 |
17 | 19 | 20 |
21 |

Actually looking at stats

22 | 28 |
29 | 
30 |         Loading live stats, please wait ~get('refresh_rate')); ?> seconds ...
31 |         
32 |
33 |
34 |
35 | SIZE 36 | Total cache size on this server 37 |
38 |
39 | %MEM 40 | Percentage of total cache size used on this server 41 |
42 |
43 | %HIT 44 | Global hit percent on this server : get_hits / (get_hits + get_misses) 45 |
46 |
47 | TIME 48 | Time taken to connect to the server and proceed the request, high value can indicate a latency or server problem 49 |
50 |
51 | REQ/s 52 | Total request per second (get, set, delete, incr, ...) issued to this server 53 |
54 |
55 | CONN 56 | Current connections, monitor that this number doesn't come too close to the server max connection setting 57 |
58 |
59 | GET/s, SET/s, DEL/s 60 | Get, set or delete commands per second issued to this server 61 |
62 |
63 | EVI/s 64 | Number of times an item which had an explicit expire time set had to be evicted before it expired 65 |
66 |
67 | READ/s 68 | Total number of bytes read by this server from network 69 |
70 |
71 | WRITE/s 72 | Total number of bytes sent by this server to network 73 |
74 |
75 |
76 | -------------------------------------------------------------------------------- /View/LiveStats/Graphic.phtml: -------------------------------------------------------------------------------- 1 | $stat) 4 | { 5 | $series[] = '{id: \'' . $name . '\', 6 | name: \'' . $name . '\', 7 | type: \'line\', 8 | pointStart: (' . (time() - (100 * $refresh_rate)) * 1000 . '), 9 | pointInterval: ' . ($refresh_rate * 1000) . ', 10 | data: ' . json_encode(array_fill(0, 100, -1)) . ', 11 | enableMouseTracking: false}'; 12 | } 13 | 14 | # Creating replacement Code for each stats by host 15 | $columns = array('REQ/s', 'EVI/s', '%MEM', 'TIME', 'CONN', 'READ/s', 'WRITE/s'); 16 | 17 | # Making line for each series 18 | foreach ($columns as $column) 19 | { 20 | # Making a replacement line for each Host 21 | foreach ($stats as $name => $stat) { 22 | $data[] = 'chart[\'' . $column . '\'].get(\'' . $name . '\').addPoint([data[\'time\'], data[\'' . $column . '\'][\'' . $name . '\']], true, true);'; 23 | } 24 | } 25 | ?> 26 |
27 |
Live Stats
28 | $_ini->get('refresh_rate')) 31 | { ?> 32 |
33 | Connections errors were discovered, to prevent any problem, refresh rate was increased by 34 | get('refresh_rate')); ?> seconds. 35 |
36 | 38 | 39 |
40 |

Actually looking at stats

41 | 45 | 46 |
47 | Loading live stats, please wait ~get('refresh_rate')); ?> seconds ... 48 |
49 |
50 | Loading live stats, please wait ~get('refresh_rate')); ?> seconds ... 51 |
52 |
53 | Loading live stats, please wait ~get('refresh_rate')); ?> seconds ... 54 |
55 |
56 | Loading live stats, please wait ~get('refresh_rate')); ?> seconds ... 57 |
58 |
59 | Loading live stats, please wait ~get('refresh_rate')); ?> seconds ... 60 |
61 |
62 | Loading live stats, please wait ~get('refresh_rate')); ?> seconds ... 63 |
64 |
65 | Loading live stats, please wait ~get('refresh_rate')); ?> seconds ... 66 |
67 |
68 |
69 |
70 | %MEM 71 | Percentage of total cache size used on this server 72 |
73 |
74 | %HIT 75 | Global hit percent on this server : get_hits / (get_hits + get_misses) 76 |
77 |
78 | TIME 79 | Time taken to connect to the server and proceed the request, high value can indicate a latency or server problem 80 |
81 |
82 | REQ/s 83 | Total request per second (get, set, delete, incr, ...) issued to this server 84 |
85 |
86 | CONN 87 | Current connections, monitor that this number doesn't come too close to the server max connection setting 88 |
89 |
90 | EVI/s 91 | Number of times an item which had an explicit expire time set had to be evicted before it expired 92 |
93 |
94 | READ/s 95 | Total number of bytes read by this server from network 96 |
97 |
98 | WRITE/s 99 | Total number of bytes sent by this server to network 100 |
101 |
102 |
103 | 104 | 105 | -------------------------------------------------------------------------------- /View/LiveStats/Stats.phtml: -------------------------------------------------------------------------------- 1 | get('refresh_rate') . ' sec)' . EOL . EOL; 7 | 8 | # Table header 9 | echo '' . sprintf('%-36s', 'NAME') . sprintf('%10s', 'SIZE') . sprintf('%7s', '%MEM') . sprintf('%8s', 'TIME') . 10 | sprintf('%6s', 'CONN') . sprintf('%7s', '%HIT') . sprintf('%8s', 'REQ/s') . sprintf('%8s', 'GET/s') . sprintf('%8s', 'SET/s') . 11 | sprintf('%8s', 'DEL/s') . sprintf('%8s', 'EVI/s') . sprintf('%11s', 'READ/s') . sprintf('%10s', 'WRITE/s') . '' . EOL . '
'; 12 | 13 | # Showing stats for every server 14 | foreach($stats as $server => $data) 15 | { 16 | # Server name 17 | echo sprintf('%-36.36s', $server); 18 | 19 | # Checking for stats validity 20 | if((isset($data['time'], $data['bytes_percent'], $data['get_hits_percent'], $data['query_time'], $data['request_rate'], $data['curr_connections'], 21 | $data['get_rate'], $data['set_rate'], $data['delete_rate'], $data['eviction_rate'], $data['bytes_read'], $data['bytes_written'])) && ($data['time'] > 0)) 22 | { 23 | # Total Memory 24 | echo sprintf('%10s', Library_Data_Analysis::byteResize($data['limit_maxbytes']) . 'b'); 25 | 26 | # Memory Occupation / Alert State 27 | if($data['bytes_percent'] > $_ini->get('memory_alert')) 28 | { 29 | echo str_pad('', 7 - strlen($data['bytes_percent']), ' ') . '' . sprintf('%.1f', $data['bytes_percent']) . ''; 30 | } 31 | else 32 | { 33 | echo sprintf('%7.1f', $data['bytes_percent']); 34 | } 35 | 36 | # Query Time 37 | echo sprintf('%5.0f', Library_Data_Analysis::valueResize($data['query_time'])) . ' ms'; 38 | 39 | # Current connection 40 | echo sprintf('%6s', $data['curr_connections']); 41 | 42 | # Hit percent (get) 43 | if($data['get_hits_percent'] < $_ini->get('hit_rate_alert')) 44 | { 45 | echo str_pad('', 7 - strlen($data['get_hits_percent']), ' ') . '' . sprintf('%.1f', $data['get_hits_percent']) . ''; 46 | } 47 | else 48 | { 49 | echo sprintf('%7.1f', $data['get_hits_percent']); 50 | } 51 | 52 | # Request rate 53 | echo sprintf('%8s', Library_Data_Analysis::valueResize($data['request_rate'])); 54 | 55 | # Get rate 56 | echo sprintf('%8s', Library_Data_Analysis::valueResize($data['get_rate'])); 57 | 58 | # Set rate 59 | echo sprintf('%8s', Library_Data_Analysis::valueResize($data['set_rate'])); 60 | 61 | # Delete rate 62 | echo sprintf('%8s', Library_Data_Analysis::valueResize($data['delete_rate'])); 63 | 64 | # Eviction rate 65 | if($data['eviction_rate'] > $_ini->get('eviction_alert')) 66 | { 67 | echo str_pad('', 8 - strlen(Library_Data_Analysis::valueResize($data['eviction_rate'])), ' ') . '' . Library_Data_Analysis::valueResize($data['eviction_rate']) . ''; 68 | } 69 | else 70 | { 71 | echo sprintf('%8s', Library_Data_Analysis::valueResize($data['eviction_rate'])); 72 | } 73 | 74 | # Bytes read 75 | echo sprintf('%11s', Library_Data_Analysis::byteResize($data['bytes_read'] / $data['time']) . 'b'); 76 | 77 | # Bytes written 78 | echo sprintf('%10s', Library_Data_Analysis::byteResize($data['bytes_written'] / $data['time']) . 'b'); 79 | } 80 | else 81 | { 82 | echo str_pad('', 20, ' ') . 'An error has occured when retreiving or calculating stats'; 83 | } 84 | 85 | # End of Line 86 | echo EOL . '
'; 87 | } -------------------------------------------------------------------------------- /View/Stats/Error.phtml: -------------------------------------------------------------------------------- 1 | 5 |
6 | cluster($_GET['server'])) ? 'All servers from Cluster ' . $_GET['server'] : 'Server ' . $_GET['server'], ' did not respond !'; 11 | } 12 | # All servers stats 13 | else 14 | { 15 | echo 'Servers did not respond !'; 16 | } ?> 17 |
18 |
19 |

Error message

20 |

21 |

Please check above error message, your configuration or your server status and retry

22 |
23 | 29 |
30 |

No slabs used in this server!

31 |
32 |
33 |

Error message

34 |

Maybe this server is not used, check your configuration or your server status and retry

35 |
36 | 42 |
43 |

No item in this slab!

44 |
45 |
46 |

Error message

47 |

This slab is allocated, but is empty

48 |

Go back to Server Slabs

49 |
50 | Console
2 |
3 |
 4 | 
 5 | 
 6 |                                          Click on an item's key below to see it's content here
 7 | 
 8 | 
9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 |
19 | Items in Slab , only showing first get('max_item_dump'); ?> items 20 | 21 | Back to Server Slabs 22 | 23 |
24 |
25 | $data) 30 | { 31 | # Checking if first item 32 | if($notFirst) { echo '
'; } 33 | ?> 34 | 70) ? substr($key, 0, 70) . '[..]' : $key); ?> 36 | 37 | 38 | Size : Bytes, 39 | Expiration : 40 | 51 | 52 | 56 |
-------------------------------------------------------------------------------- /View/Stats/Slabs.phtml: -------------------------------------------------------------------------------- 1 |
2 |
Slabs Stats
3 |
4 |
5 | Slabs Used 6 | 7 |
8 |
9 | Memory Used 10 | Bytes 11 |
12 |
13 | Wasted 14 | Bytes 15 |
16 |
17 |
18 | 19 |
20 | 23 |
24 | 25 |
26 |
27 |

For more informations about memcached slabs stats, see memcached protocol 28 | here 29 |

30 |
31 |
32 | 33 | 34 | 35 | $slab) 40 | { 41 | # If Slab is Used 42 | if(is_numeric($id)) 43 | { 44 | # Making a new line 45 | if($actualSlab >= 4) 46 | { 47 | ?> 48 | 49 | 50 | 54 | 114 | 118 | '; 123 | } 124 | ?> 125 | 126 |
0) { echo 'style="padding-left:9px;"'; } ?> valign="top"> 55 |
Slab Stats 56 | See Slab Items 57 |
58 |
59 |
60 | Chunk Size 61 | Bytes 62 |
63 |
64 | Used Chunk 65 | 66 | [ %] 67 |
68 |
69 | Total Chunk 70 | 71 |
72 |
73 | Total Page 74 | 75 |
76 |
77 | Wasted 78 | Bytes 79 |
80 |
81 | Hits 82 | 999) ? Library_Data_Analysis::hitResize($slab['request_rate']) : $slab['request_rate']; ?> Request/sec 83 |
84 | 0) 86 | { ?> 87 |
88 | Evicted 89 | 90 |
91 | 105 | 108 |
109 | Slab is allocated but empty 110 |
111 | 112 |
113 |
-------------------------------------------------------------------------------- /commands.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015-2017 WP-Cloud 5 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 6 | * @package phpMemcachedAdmin 7 | */ 8 | 9 | /** 10 | * Executing commands 11 | * 12 | * @since 06/04/2010 13 | */ 14 | # Headers 15 | header('Content-type: text/html;'); 16 | header('Cache-Control: no-cache, must-revalidate'); 17 | 18 | # Require 19 | require_once 'Library/Loader.php'; 20 | 21 | # Date timezone 22 | date_default_timezone_set('Europe/Paris'); 23 | 24 | # Loading ini file 25 | $_ini = Library_Configuration_Loader::singleton(); 26 | 27 | # Initializing requests & response 28 | $request = (isset($_GET['request_command'])) ? $_GET['request_command'] : null; 29 | 30 | # Starting 31 | ob_start(); 32 | 33 | # Display by request rype 34 | switch($request) 35 | { 36 | # Memcache::get command 37 | case 'get': 38 | # Ask for get on a cluster 39 | if(isset($_GET['request_server']) && ($cluster = $_ini->cluster($_GET['request_server']))) 40 | { 41 | foreach($cluster as $server) 42 | { 43 | # Dumping server get command response 44 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 45 | Library_Command_Factory::api($_GET['request_api'])->get($server['hostname'], $server['port'], $_GET['request_key'])); 46 | } 47 | } 48 | # Ask for get on one server 49 | elseif(isset($_GET['request_server']) && ($server = $_ini->server($_GET['request_server']))) 50 | { 51 | # Dumping server get command response 52 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 53 | Library_Command_Factory::api($_GET['request_api'])->get($server['hostname'], $server['port'], $_GET['request_key'])); 54 | } 55 | # Ask for get on all servers 56 | else 57 | { 58 | foreach($_ini->get('servers') as $cluster => $servers) 59 | { 60 | # Asking for each server stats 61 | foreach($servers as $server) 62 | { 63 | # Dumping server get command response 64 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 65 | Library_Command_Factory::api($_GET['request_api'])->get($server['hostname'], $server['port'], $_GET['request_key'])); 66 | } 67 | } 68 | } 69 | break; 70 | 71 | # Memcache::set command 72 | case 'set': 73 | # Ask for set on a cluster 74 | if(isset($_GET['request_server']) && ($cluster = $_ini->cluster($_GET['request_server']))) 75 | { 76 | foreach($cluster as $server) 77 | { 78 | # Dumping server get command response 79 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 80 | Library_Command_Factory::api($_GET['request_api'])->set($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_data'], $_GET['request_duration'])); 81 | } 82 | } 83 | # Ask for set on one server 84 | elseif(isset($_GET['request_server']) && ($server = $_ini->server($_GET['request_server']))) 85 | { 86 | # Dumping server set command response 87 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 88 | Library_Command_Factory::api($_GET['request_api'])->set($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_data'], $_GET['request_duration'])); 89 | } 90 | # Ask for set on all servers 91 | else 92 | { 93 | foreach($_ini->get('servers') as $cluster => $servers) 94 | { 95 | # Asking for each server stats 96 | foreach($servers as $server) 97 | { 98 | # Dumping server set command response 99 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 100 | Library_Command_Factory::api($_GET['request_api'])->set($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_data'], $_GET['request_duration'])); 101 | } 102 | } 103 | } 104 | break; 105 | 106 | # Memcache::delete command 107 | case 'delete': 108 | # Ask for delete on a cluster 109 | if(isset($_GET['request_server']) && ($cluster = $_ini->cluster($_GET['request_server']))) 110 | { 111 | foreach($cluster as $server) 112 | { 113 | # Dumping server get command response 114 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 115 | Library_Command_Factory::api($_GET['request_api'])->delete($server['hostname'], $server['port'], $_GET['request_key'])); 116 | } 117 | } 118 | # Ask for delete on one server 119 | elseif(isset($_GET['request_server']) && ($server = $_ini->server($_GET['request_server']))) 120 | { 121 | # Dumping server delete command response 122 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 123 | Library_Command_Factory::api($_GET['request_api'])->delete($server['hostname'], $server['port'], $_GET['request_key'])); 124 | } 125 | # Ask for delete on all servers 126 | else 127 | { 128 | foreach($_ini->get('servers') as $cluster => $servers) 129 | { 130 | # Asking for each server stats 131 | foreach($servers as $server) 132 | { 133 | # Dumping server delete command response 134 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 135 | Library_Command_Factory::api($_GET['request_api'])->delete($server['hostname'], $server['port'], $_GET['request_key'])); 136 | } 137 | } 138 | } 139 | break; 140 | 141 | # Memcache::increment command 142 | case 'increment': 143 | # Checking value 144 | if(!isset($_GET['request_value']) || !is_numeric($_GET['request_value'])) 145 | { 146 | $_GET['request_value'] = 1; 147 | } 148 | 149 | # Ask for increment on a cluster 150 | if(isset($_GET['request_server']) && ($cluster = $_ini->cluster($_GET['request_server']))) 151 | { 152 | foreach($cluster as $server) 153 | { 154 | # Dumping server increment command response 155 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 156 | Library_Command_Factory::api($_GET['request_api'])->increment($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_value'])); 157 | } 158 | } 159 | # Ask for increment on one server 160 | elseif(isset($_GET['request_server']) && ($server = $_ini->server($_GET['request_server']))) 161 | { 162 | # Dumping server increment command response 163 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 164 | Library_Command_Factory::api($_GET['request_api'])->increment($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_value'])); 165 | } 166 | # Ask for increment on all servers 167 | else 168 | { 169 | foreach($_ini->get('servers') as $cluster => $servers) 170 | { 171 | # Asking for each server stats 172 | foreach($servers as $server) 173 | { 174 | # Dumping server increment command response 175 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 176 | Library_Command_Factory::api($_GET['request_api'])->increment($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_value'])); 177 | } 178 | } 179 | } 180 | break; 181 | 182 | # Memcache::decrement command 183 | case 'decrement': 184 | # Checking value 185 | if(!isset($_GET['request_value']) || !is_numeric($_GET['request_value'])) 186 | { 187 | $_GET['request_value'] = 1; 188 | } 189 | 190 | # Ask for decrement on a cluster 191 | if(isset($_GET['request_server']) && ($cluster = $_ini->cluster($_GET['request_server']))) 192 | { 193 | foreach($cluster as $server) 194 | { 195 | # Dumping server decrement command response 196 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 197 | Library_Command_Factory::api($_GET['request_api'])->decrement($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_value'])); 198 | } 199 | } 200 | # Ask for decrement on one server 201 | elseif(isset($_GET['request_server']) && ($server = $_ini->server($_GET['request_server']))) 202 | { 203 | # Dumping server decrement command response 204 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 205 | Library_Command_Factory::api($_GET['request_api'])->decrement($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_value'])); 206 | } 207 | # Ask for decrement on all servers 208 | else 209 | { 210 | foreach($_ini->get('servers') as $cluster => $servers) 211 | { 212 | # Asking for each server stats 213 | foreach($servers as $server) 214 | { 215 | # Dumping server decrement command response 216 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 217 | Library_Command_Factory::api($_GET['request_api'])->decrement($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_value'])); 218 | } 219 | } 220 | } 221 | break; 222 | 223 | # Memcache::flush_all command 224 | case 'flush_all': 225 | # Checking delay 226 | if(!isset($_GET['request_delay']) || !is_numeric($_GET['request_delay'])) 227 | { 228 | $_GET['request_delay'] = 0; 229 | } 230 | 231 | # Ask for flush_all on a cluster 232 | if(isset($_GET['request_server']) && ($cluster = $_ini->cluster($_GET['request_server']))) 233 | { 234 | foreach($cluster as $server) 235 | { 236 | # Dumping server get command response 237 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 238 | Library_Command_Factory::api($_GET['request_api'])->flush_all($server['hostname'], $server['port'], $_GET['request_delay'])); 239 | } 240 | } 241 | # Ask for flush_all on one server 242 | elseif(isset($_GET['request_server']) && ($server = $_ini->server($_GET['request_server']))) 243 | { 244 | # Dumping server flush_all command response 245 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 246 | Library_Command_Factory::api($_GET['request_api'])->flush_all($server['hostname'], $server['port'], $_GET['request_delay'])); 247 | } 248 | # Ask for flush_all on all servers 249 | else 250 | { 251 | foreach($_ini->get('servers') as $cluster => $servers) 252 | { 253 | # Asking for each server stats 254 | foreach($servers as $server) 255 | { 256 | # Dumping server flush_all command response 257 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 258 | Library_Command_Factory::api($_GET['request_api'])->flush_all($server['hostname'], $server['port'], $_GET['request_delay'])); 259 | } 260 | } 261 | } 262 | break; 263 | 264 | # Memcache::search command 265 | case 'search': 266 | # Ask for search on a cluster 267 | if(isset($_GET['request_server']) && ($cluster = $_ini->cluster($_GET['request_server']))) 268 | { 269 | foreach($cluster as $server) 270 | { 271 | # Dumping server get command response 272 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 273 | Library_Command_Factory::api('Server')->search($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_level'], $_GET['request_more'])); 274 | } 275 | } 276 | # Ask for search on one server 277 | elseif(isset($_GET['request_server']) && ($server = $_ini->server($_GET['request_server']))) 278 | { 279 | # Dumping server search command response 280 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 281 | Library_Command_Factory::api('Server')->search($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_level'], $_GET['request_more'])); 282 | } 283 | # Ask for search on all servers 284 | else 285 | { 286 | # Looking into each cluster 287 | foreach($_ini->get('servers') as $cluster => $servers) 288 | { 289 | # Asking for each server stats 290 | foreach($servers as $server) 291 | { 292 | # Dumping server search command response 293 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 294 | Library_Command_Factory::api('Server')->search($server['hostname'], $server['port'], $_GET['request_key'], $_GET['request_level'], $_GET['request_more'])); 295 | } 296 | } 297 | } 298 | break; 299 | 300 | # Memcache::telnet command 301 | case 'telnet': 302 | # Ask for a telnet command on a cluster 303 | if(isset($_GET['request_server']) && ($cluster = $_ini->cluster($_GET['request_server']))) 304 | { 305 | foreach($cluster as $server) 306 | { 307 | # Dumping server telnet command response 308 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 309 | Library_Command_Factory::api('Server')->telnet($server['hostname'], $server['port'], $_GET['request_telnet'])); 310 | } 311 | } 312 | # Ask for a telnet command on one server 313 | elseif(isset($_GET['request_server']) && ($server = $_ini->server($_GET['request_server']))) 314 | { 315 | # Dumping server telnet command response 316 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 317 | Library_Command_Factory::api('Server')->telnet($server['hostname'], $server['port'], $_GET['request_telnet'])); 318 | } 319 | # Ask for a telnet command on all servers 320 | else 321 | { 322 | # Looking into each cluster 323 | foreach($_ini->get('servers') as $cluster => $servers) 324 | { 325 | # Asking for each server stats 326 | foreach($servers as $server) 327 | { 328 | # Dumping server telnet command response 329 | echo Library_HTML_Components::serverResponse($server['hostname'], $server['port'], 330 | Library_Command_Factory::api('Server')->telnet($server['hostname'], $server['port'], $_GET['request_telnet'])); 331 | } 332 | } 333 | } 334 | break; 335 | # Default : No command 336 | default : 337 | # Showing header 338 | include 'View/Header.phtml'; 339 | 340 | # Showing formulary 341 | include 'View/Commands/Commands.phtml'; 342 | 343 | # Showing footer 344 | include 'View/Footer.phtml'; 345 | break; 346 | } 347 | 348 | ob_end_flush(); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wp-cloud/phpmemcacheadmin", 3 | "type": "application", 4 | "description": "Graphic stand-alone administration for memcached to monitor and debug purpose", 5 | "keywords": ["phpmemcacheadmin","memcache","memcached","web"], 6 | "homepage": "https://github.com/wp-cloud/phpmemcacheadmin", 7 | "license": "GPL-2.0+", 8 | "authors": [ 9 | { 10 | "name": "Cyrille Mahieux", 11 | "email": "elijaa@free.fr", 12 | "homepage": "http://blog.elijaa.org/" 13 | }, 14 | { 15 | "name": "Christian Foellmann", 16 | "email": "foellmann@foe-services.de", 17 | "homepage": "http://christian.foellmann.de/" 18 | } 19 | ], 20 | "support": { 21 | "issues": "https://github.com/wp-cloud/phpmemcacheadmin/issues", 22 | "wiki": "https://github.com/wp-cloud/phpmemcacheadmin/wiki", 23 | "source": "https://github.com/wp-cloud/phpmemcacheadmin" 24 | }, 25 | "require": { 26 | "php": ">=5.3.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /configure.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015-2017 WP-Cloud 5 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 6 | * @package phpMemcachedAdmin 7 | */ 8 | 9 | /** 10 | * Configuration 11 | * 12 | * @since 06/04/2010 13 | */ 14 | # Headers 15 | header('Content-type: text/html;'); 16 | header('Cache-Control: no-cache, must-revalidate'); 17 | 18 | # Require 19 | require_once 'Library/Loader.php'; 20 | 21 | # Date timezone 22 | date_default_timezone_set('Europe/Paris'); 23 | 24 | # Loading ini file 25 | $_ini = Library_Configuration_Loader::singleton(); 26 | 27 | # Initializing requests 28 | $request = (isset($_GET['request_write'])) ? $_GET['request_write'] : null; 29 | $write = null; 30 | 31 | # Display by request rype 32 | switch($request) 33 | { 34 | # Unlock configuration file & temp directory 35 | case 'unlock': 36 | # chmod 0755 37 | chmod(Library_Configuration_Loader::path(), 0755); 38 | chmod($_ini->get('file_path'), 0755); 39 | break; 40 | 41 | # Live stats configuration save 42 | case 'live_stats': 43 | # Updating configuration 44 | $_ini->set('refresh_rate', round(max(2, $_POST['refresh_rate']))); 45 | $_ini->set('memory_alert', $_POST['memory_alert']); 46 | $_ini->set('hit_rate_alert', $_POST['hit_rate_alert']); 47 | $_ini->set('eviction_alert', $_POST['eviction_alert']); 48 | $_ini->set('file_path', $_POST['file_path']); 49 | 50 | # Writing configuration file 51 | $write = Library_Configuration_Loader::singleton()->write(); 52 | break; 53 | 54 | # Commands configuration save 55 | case 'commands': 56 | # Updating configuration 57 | $_ini->set('stats_api', $_POST['stats_api']); 58 | $_ini->set('slabs_api', $_POST['slabs_api']); 59 | $_ini->set('items_api', $_POST['items_api']); 60 | $_ini->set('get_api', $_POST['get_api']); 61 | $_ini->set('set_api', $_POST['set_api']); 62 | $_ini->set('delete_api', $_POST['delete_api']); 63 | $_ini->set('flush_all_api', $_POST['flush_all_api']); 64 | 65 | # Writing configuration file 66 | $write = Library_Configuration_Loader::singleton()->write(); 67 | break; 68 | 69 | # Server configuration save 70 | case 'servers': 71 | $array = array(); 72 | foreach($_POST['server'] as $cluster => $servers) 73 | { 74 | foreach($servers as $data) 75 | { 76 | $array[$_POST['cluster'][$cluster]][$data['name']] = $data; 77 | unset($array[$_POST['cluster'][$cluster]][$data['name']]['name']); 78 | } 79 | } 80 | 81 | # Sorting clusters 82 | ksort($array); 83 | foreach($array as $cluster => $servers) 84 | { 85 | # Sorting servers 86 | ksort($servers); 87 | $array[$cluster] = $servers; 88 | } 89 | 90 | # Updating configuration 91 | $_ini->set('servers', $array); 92 | 93 | # Writing configuration file 94 | $write = Library_Configuration_Loader::singleton()->write(); 95 | break; 96 | 97 | # Miscellaneous configuration save 98 | case 'miscellaneous': 99 | # Updating configuration 100 | $_ini->set('connection_timeout', $_POST['connection_timeout']); 101 | $_ini->set('max_item_dump', $_POST['max_item_dump']); 102 | 103 | # Writing configuration file 104 | $write = Library_Configuration_Loader::singleton()->write(); 105 | break; 106 | 107 | # Default : No command 108 | default : 109 | break; 110 | } 111 | 112 | # Showing header 113 | include 'View/Header.phtml'; 114 | 115 | # Showing formulary 116 | include 'View/Configure/Configure.phtml'; 117 | 118 | # Showing footer 119 | include 'View/Footer.phtml'; -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015-2017 WP-Cloud 5 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 6 | * @package phpMemcachedAdmin 7 | * @version 1.3.0-dev 8 | * 9 | * 10 | * Copyright 2010-2015 Cyrille Mahieux 11 | * Copyright 2015-2017 WP-Cloud 12 | * 13 | * Licensed under the Apache License, Version 2.0 (the "License"); 14 | * you may not use this file except in compliance with the License. 15 | * You may obtain a copy of the License at 16 | * 17 | * http://www.apache.org/licenses/LICENSE-2.0 18 | * 19 | * Unless required by applicable law or agreed to in writing, software 20 | * distributed under the License is distributed on an "AS IS" BASIS, 21 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22 | * See the License for the specific language governing permissions and limitations 23 | * under the License. 24 | * 25 | * ><)))°> ><)))°> ><)))°> ><)))°> ><)))°> ><)))°> ><)))°> ><)))°> ><)))°> 26 | */ 27 | 28 | /** 29 | * Stats viewing 30 | * 31 | * @since 20/03/2010 32 | */ 33 | # Headers 34 | header('Content-type: text/html; charset=UTF-8'); 35 | header('Cache-Control: no-cache, must-revalidate'); 36 | 37 | # Require 38 | require_once 'Library/Loader.php'; 39 | 40 | # Date timezone 41 | date_default_timezone_set('Europe/Paris'); 42 | 43 | # Loading ini file 44 | $_ini = Library_Configuration_Loader::singleton(); 45 | 46 | # Initializing requests 47 | $request = (isset($_GET['show'])) ? $_GET['show'] : null; 48 | 49 | # Getting default cluster 50 | if(!isset($_GET['server'])) 51 | { 52 | $clusters = array_keys($_ini->get('servers')); 53 | $cluster = isset($clusters[0]) ? $clusters[0] : null; 54 | $_GET['server'] = $cluster; 55 | } 56 | 57 | # Showing header 58 | include 'View/Header.phtml'; 59 | 60 | # Display by request type 61 | switch($request) 62 | { 63 | # Items : Display of all items for a single slab for a single server 64 | case 'items': 65 | # Initializing items array 66 | $server = null; 67 | $items = false; 68 | 69 | # Ask for one server and one slabs items 70 | if(isset($_GET['server']) && ($server = $_ini->server($_GET['server']))) 71 | { 72 | $items = Library_Command_Factory::instance('items_api')->items($server['hostname'], $server['port'], $_GET['slab']); 73 | } 74 | 75 | # Getting stats to calculate server boot time 76 | $stats = Library_Command_Factory::instance('stats_api')->stats($server['hostname'], $server['port']); 77 | $infinite = (isset($stats['time'], $stats['uptime'])) ? ($stats['time'] - $stats['uptime']) : 0; 78 | 79 | # Items are well formed 80 | if($items !== false) 81 | { 82 | # Showing items 83 | include 'View/Stats/Items.phtml'; 84 | } 85 | # Items are not well formed 86 | else 87 | { 88 | include 'View/Stats/Error.phtml'; 89 | } 90 | unset($items); 91 | break; 92 | 93 | # Slabs : Display of all slabs for a single server 94 | case 'slabs': 95 | # Initializing slabs array 96 | $slabs = false; 97 | 98 | # Ask for one server slabs 99 | if(isset($_GET['server']) && ($server = $_ini->server($_GET['server']))) 100 | { 101 | # Spliting server in hostname:port 102 | $slabs = Library_Command_Factory::instance('slabs_api')->slabs($server['hostname'], $server['port']); 103 | } 104 | 105 | # Slabs are well formed 106 | if($slabs !== false) 107 | { 108 | # Analysis 109 | $slabs = Library_Data_Analysis::slabs($slabs); 110 | include 'View/Stats/Slabs.phtml'; 111 | } 112 | # Slabs are not well formed 113 | else 114 | { 115 | include 'View/Stats/Error.phtml'; 116 | } 117 | unset($slabs); 118 | break; 119 | 120 | # Default : Stats for all or specific single server 121 | default : 122 | # Initializing stats & settings array 123 | $stats = array(); 124 | $slabs = array(); 125 | $slabs['total_malloced'] = 0; 126 | $slabs['total_wasted'] = 0; 127 | $settings = array(); 128 | $status = array(); 129 | 130 | $cluster = null; 131 | $server = null; 132 | 133 | # Ask for a particular cluster stats 134 | if(isset($_GET['server']) && ($cluster = $_ini->cluster($_GET['server']))) 135 | { 136 | foreach($cluster as $name => $server) 137 | { 138 | # Getting Stats & Slabs stats 139 | $data = array(); 140 | $data['stats'] = Library_Command_Factory::instance('stats_api')->stats($server['hostname'], $server['port']); 141 | $data['slabs'] = Library_Data_Analysis::slabs(Library_Command_Factory::instance('slabs_api')->slabs($server['hostname'], $server['port'])); 142 | $stats = Library_Data_Analysis::merge($stats, $data['stats']); 143 | 144 | # Computing stats 145 | if(isset($data['slabs']['total_malloced'], $data['slabs']['total_wasted'])) 146 | { 147 | $slabs['total_malloced'] += $data['slabs']['total_malloced']; 148 | $slabs['total_wasted'] += $data['slabs']['total_wasted']; 149 | } 150 | $status[$name] = ($data['stats'] != array()) ? $data['stats']['version'] : ''; 151 | $uptime[$name] = ($data['stats'] != array()) ? $data['stats']['uptime'] : ''; 152 | } 153 | } 154 | # Asking for a server stats 155 | elseif(isset($_GET['server']) && ($server = $_ini->server($_GET['server']))) 156 | { 157 | # Getting Stats & Slabs stats 158 | $stats = Library_Command_Factory::instance('stats_api')->stats($server['hostname'], $server['port']); 159 | $slabs = Library_Data_Analysis::slabs(Library_Command_Factory::instance('slabs_api')->slabs($server['hostname'], $server['port'])); 160 | $settings = Library_Command_Factory::instance('stats_api')->settings($server['hostname'], $server['port']); 161 | } 162 | 163 | # Stats are well formed 164 | if(($stats !== false) && ($stats != array())) 165 | { 166 | # Analysis 167 | $stats = Library_Data_Analysis::stats($stats); 168 | include 'View/Stats/Stats.phtml'; 169 | } 170 | # Stats are not well formed 171 | else 172 | { 173 | include 'View/Stats/Error.phtml'; 174 | } 175 | unset($stats); 176 | break; 177 | } 178 | # Showing footer 179 | include 'View/Footer.phtml'; -------------------------------------------------------------------------------- /spam.php: -------------------------------------------------------------------------------- 1 | 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 | while(true) 9 | { 10 | $handle = fsockopen('127.0.0.1', 11211, $errno, $errstr, 2); 11 | for($i = rand(0, 200) ; $i < 300 ; $i++) 12 | { 13 | fwrite($handle, 'set ' . md5(microtime(true) . rand(0, 250000000)) . ' 0 18000 10' . "\r\n"); 14 | fwrite($handle, 'aaaaaaaaaa' . "\r\n"); 15 | } 16 | usleep(rand(0, 10000)); 17 | set_time_limit(5); 18 | //sleep(rand(0,10)); 19 | } -------------------------------------------------------------------------------- /stats.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015-2017 WP-Cloud 5 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 6 | * @package phpMemcachedAdmin 7 | */ 8 | 9 | /** 10 | * @since 12/04/2010 11 | */ 12 | # Headers 13 | header('Content-type: text/html;'); 14 | header('Cache-Control: no-cache, must-revalidate'); 15 | 16 | # Require 17 | require_once 'Library/Loader.php'; 18 | 19 | # Loading ini file 20 | $_ini = Library_Configuration_Loader::singleton(); 21 | 22 | # Initializing requests 23 | $request = (isset($_GET['request_command'])) ? $_GET['request_command'] : null; 24 | 25 | # Stat of a particular cluster 26 | if(isset($_GET['cluster']) && ($_GET['cluster'] != null)) 27 | { 28 | $cluster = $_GET['cluster']; 29 | } 30 | # Getting default cluster 31 | else 32 | { 33 | $clusters = array_keys($_ini->get('servers')); 34 | $cluster = isset($clusters[0]) ? $clusters[0] : null; 35 | $_GET['cluster'] = $cluster; 36 | } 37 | 38 | /** In Progress 39 | # Getting view mode 40 | if(isset($_GET['mode']) && (($_GET['mode'] == 'console') || ($_GET['mode'] == 'graphic'))) 41 | { 42 | $mode = $_GET['mode']; 43 | } 44 | else 45 | { 46 | $mode = 'graphic'; 47 | } 48 | */ 49 | 50 | # Hashing cluster 51 | $hash = md5($_GET['cluster']); 52 | 53 | # Cookie @FIXME not a perfect method 54 | if(!isset($_COOKIE['live_stats_id' . $hash])) 55 | { 56 | # Cleaning temporary directory 57 | $files = glob($_ini->get('file_path') . '*', GLOB_NOSORT); 58 | foreach($files as $path) 59 | { 60 | # Getting file last modification time 61 | $stats = @stat($path); 62 | 63 | # Deleting file older than 24 hours 64 | if(isset($stats['mtime']) && ($stats['mtime'] < (time() - 60*60*24))) 65 | { 66 | @unlink($path); 67 | } 68 | } 69 | 70 | # Generating unique id 71 | $live_stats_id = rand() . $hash; 72 | 73 | # Cookie 74 | setcookie('live_stats_id' . $hash, $live_stats_id, time() + 60*60*24); 75 | } 76 | else 77 | { 78 | # Backup from a previous request 79 | $live_stats_id = $_COOKIE['live_stats_id' . $hash]; 80 | } 81 | 82 | # Live stats dump file 83 | $file_path = rtrim($_ini->get('file_path'), '/') . DIRECTORY_SEPARATOR . 'live_stats.' . $live_stats_id; 84 | 85 | # Display by request type 86 | switch($request) 87 | { 88 | # Ajax ask : stats 89 | case 'live_stats': 90 | # Opening old stats dump 91 | $previous = @unserialize(file_get_contents($file_path)); 92 | 93 | # Initializing variables 94 | $actual = array(); 95 | $stats = array(); 96 | $time = 0; 97 | 98 | # Requesting stats for each server 99 | foreach($_ini->cluster($cluster) as $name => $server) 100 | { 101 | # Start query time calculation 102 | $time = microtime(true); 103 | 104 | # Asking server for stats 105 | $actual[$name] = Library_Command_Factory::instance('stats_api')->stats($server['hostname'], $server['port']); 106 | 107 | # Calculating query time length 108 | $actual[$name]['query_time'] = max((microtime(true) - $time) * 1000, 1); 109 | } 110 | 111 | # Analysing stats 112 | foreach($_ini->cluster($cluster) as $name => $server) 113 | { 114 | # Making an alias @FIXME Used ? 115 | $server = $name; 116 | 117 | # Diff between old and new dump 118 | $stats[$server] = Library_Data_Analysis::diff($previous[$server], $actual[$server]); 119 | } 120 | 121 | # Making stats for each server 122 | foreach($stats as $server => $array) 123 | { 124 | # Analysing request 125 | if((isset($stats[$server]['uptime'])) && ($stats[$server]['uptime'] > 0)) 126 | { 127 | # Computing stats 128 | $stats[$server] = Library_Data_Analysis::stats($stats[$server]); 129 | 130 | # Because we make a diff on every key, we must reasign some values 131 | $stats[$server]['bytes_percent'] = sprintf('%.1f', $actual[$server]['bytes'] / $actual[$server]['limit_maxbytes'] * 100); 132 | $stats[$server]['bytes'] = $actual[$server]['bytes']; 133 | $stats[$server]['limit_maxbytes'] = $actual[$server]['limit_maxbytes']; 134 | $stats[$server]['curr_connections'] = $actual[$server]['curr_connections']; 135 | $stats[$server]['query_time'] = $actual[$server]['query_time']; 136 | } 137 | } 138 | 139 | # Saving new stats dump 140 | file_put_contents($file_path, serialize($actual)); 141 | 142 | # Showing stats 143 | include 'View/LiveStats/Stats.phtml'; 144 | break; 145 | 146 | # Default : No command 147 | default : 148 | # Initializing : making stats dump 149 | $stats = array(); 150 | foreach($_ini->cluster($cluster) as $name => $server) 151 | { 152 | $stats[$name] = Library_Command_Factory::instance('stats_api')->stats($server['hostname'], $server['port']); 153 | } 154 | 155 | # Saving first stats dump 156 | file_put_contents($file_path, serialize($stats)); 157 | 158 | # Searching for connection error, adding some time to refresh rate to prevent error 159 | $refresh_rate = max($_ini->get('refresh_rate'), count($_ini->cluster($cluster)) * 0.25 + (Library_Data_Error::count() * (0.5 + $_ini->get('connection_timeout')))); 160 | 161 | # Showing header 162 | include 'View/Header.phtml'; 163 | 164 | # Showing live stats frame 165 | include 'View/LiveStats/Frame.phtml'; 166 | # include 'View/LiveStats/Graphic.phtml'; 167 | 168 | # Showing footer 169 | include 'View/Footer.phtml'; 170 | 171 | break; 172 | } --------------------------------------------------------------------------------