| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  | <?php | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  | // This file is part of GNU social - https://www.gnu.org/software/social
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GNU social is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GNU social is distributed in the hope that it will be useful,
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with GNU social.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |  * A plugin to use memcached for the interface with memcache | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |  * | 
					
						
							|  |  |  |  * @category  Cache | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |  * @package   GNUsocial | 
					
						
							| 
									
										
										
										
											2010-05-27 18:26:47 -04:00
										 |  |  |  * @author    Evan Prodromou <evan@status.net> | 
					
						
							|  |  |  |  * @author    Craig Andrews <candrews@integralblue.com> | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |  * @author    Miguel Dantas <biodantas@gmail.com> | 
					
						
							|  |  |  |  * @copyright 2009, 2019 Free Software Foundation, Inc http://www.fsf.org | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  | defined('GNUSOCIAL') || die(); | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * A plugin to use memcached for the cache interface | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This used to be encoded as config-variable options in the core code; | 
					
						
							|  |  |  |  * it's now broken out to a separate plugin. The same interface can be | 
					
						
							|  |  |  |  * implemented by other plugins. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @copyright 2009, 2019 Free Software Foundation, Inc http://www.fsf.org | 
					
						
							|  |  |  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  | class MemcachedPlugin extends Plugin | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |     const PLUGIN_VERSION = '2.1.0'; | 
					
						
							| 
									
										
										
										
											2019-06-03 01:56:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     public static $cacheInitialized = false; | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |     public $servers = ['127.0.0.1']; | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     public $defaultExpiry = 86400; // 24h
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     private $_conn = null; | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Initialize the plugin | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Note that onStartCacheGet() may have been called before this! | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |      * @return bool flag value | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     public function initialize(): bool | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-08-15 16:17:49 +01:00
										 |  |  |         if (self::$cacheInitialized) { | 
					
						
							|  |  |  |             $this->persistent = true; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             // If we're a parent command-line process we need
 | 
					
						
							|  |  |  |             // to be able to close out the connection after
 | 
					
						
							|  |  |  |             // forking, so disable persistence.
 | 
					
						
							|  |  |  |             //
 | 
					
						
							|  |  |  |             // We'll turn it back on again the second time
 | 
					
						
							|  |  |  |             // through which will either be in a child process,
 | 
					
						
							|  |  |  |             // or a single-process script which is switching
 | 
					
						
							|  |  |  |             // configurations.
 | 
					
						
							|  |  |  |             $this->persistent = (php_sapi_name() == 'cli') ? false : true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |         try { | 
					
						
							|  |  |  |             $this->_ensureConn(); | 
					
						
							|  |  |  |             self::$cacheInitialized = true; | 
					
						
							|  |  |  |         } catch (MemcachedException $e) { | 
					
						
							|  |  |  |             common_log(LOG_ERR, 'Memcached encountered exception ' . get_class($e) . ': ' . $e->getMessage()); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get a value associated with a key | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * The value should have been set previously. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |      * @param string &$key in; Lookup key | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |      * @param mixed  &$value out; value associated with key | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |      * @return bool hook success | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     public function onStartCacheGet(&$key, &$value): bool | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |         try { | 
					
						
							|  |  |  |             $this->_ensureConn(); | 
					
						
							|  |  |  |             $value = $this->_conn->get($key); | 
					
						
							|  |  |  |         } catch (MemcachedException $e) { | 
					
						
							|  |  |  |             common_log(LOG_ERR, 'Memcached encountered exception ' . get_class($e) . ': ' . $e->getMessage()); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if ($value === false) { | 
					
						
							|  |  |  |             // If not found, let other plugins handle it
 | 
					
						
							|  |  |  |             return $this->_conn->getResultCode() === Memcached::RES_NOTFOUND; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Associate a value with a key | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |      * @param string  &$key in; Key to use for lookups | 
					
						
							|  |  |  |      * @param mixed   &$value in; Value to associate | 
					
						
							|  |  |  |      * @param integer &$flag in; Flag empty or Memcached::OPT_COMPRESSION (translated by the `flag` method) | 
					
						
							|  |  |  |      * @param integer &$expiry in; Expiry (passed through to Memcache) | 
					
						
							|  |  |  |      * @param bool &$success out; Whether the set was successful | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |      * @return bool hook success | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     public function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success): bool | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     { | 
					
						
							|  |  |  |         if ($expiry === null) { | 
					
						
							|  |  |  |             $expiry = $this->defaultExpiry; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |         try { | 
					
						
							|  |  |  |             $this->_ensureConn(); | 
					
						
							| 
									
										
										
										
											2019-08-15 16:17:49 +01:00
										 |  |  |             if (!empty($flag)) { | 
					
						
							|  |  |  |                 $this->_conn->setOption(Memcached::OPT_COMPRESSION, $flag); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |             $success = $this->_conn->set($key, $value, $expiry); | 
					
						
							|  |  |  |         } catch (MemcachedException $e) { | 
					
						
							|  |  |  |             common_log(LOG_ERR, 'Memcached encountered exception ' . get_class($e) . ': ' . $e->getMessage()); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return !$success; | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Atomically increment an existing numeric key value. | 
					
						
							|  |  |  |      * Existing expiration time will not be changed. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |      * @param string &$key in; Key to use for lookups | 
					
						
							|  |  |  |      * @param int    &$step in; Amount to increment (default 1) | 
					
						
							|  |  |  |      * @param mixed  &$value out; Incremented value, or false if key not set. | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |      * @return bool hook success | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     public function onStartCacheIncrement(&$key, &$step, &$value): bool | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |         try { | 
					
						
							|  |  |  |             $this->_ensureConn(); | 
					
						
							|  |  |  |             $value = $this->_conn->increment($key, $step); | 
					
						
							|  |  |  |         } catch (MemcachedException $e) { | 
					
						
							|  |  |  |             common_log(LOG_ERR, 'Memcached encountered exception ' . get_class($e) . ': ' . $e->getMessage()); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if ($value === false) { | 
					
						
							|  |  |  |             // If not found, let other plugins handle it
 | 
					
						
							|  |  |  |             return $this->_conn->getResultCode() === Memcached::RES_NOTFOUND; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Delete a value associated with a key | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |      * @param string  &$key in; Key to lookup | 
					
						
							|  |  |  |      * @param bool &$success out; whether it worked | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |      * @return bool hook success | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     public function onStartCacheDelete(&$key, &$success): bool | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |         try { | 
					
						
							|  |  |  |             $this->_ensureConn(); | 
					
						
							|  |  |  |             $success = $this->_conn->delete($key); | 
					
						
							|  |  |  |         } catch (MemcachedException $e) { | 
					
						
							|  |  |  |             common_log(LOG_ERR, 'Memcached encountered exception ' . get_class($e) . ': ' . $e->getMessage()); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return !$success; | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * @param $success | 
					
						
							|  |  |  |      * @return bool | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function onStartCacheReconnect(&$success): bool | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-08-15 16:17:49 +01:00
										 |  |  |         if (empty($this->_conn)) { | 
					
						
							|  |  |  |             // nothing to do
 | 
					
						
							|  |  |  |             return true; | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-08-15 16:17:49 +01:00
										 |  |  |         if ($this->persistent) { | 
					
						
							|  |  |  |             common_log(LOG_ERR, "Cannot close persistent memcached connection"); | 
					
						
							|  |  |  |             $success = false; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             common_log(LOG_INFO, "Closing memcached connection"); | 
					
						
							| 
									
										
										
										
											2019-09-02 16:42:10 +01:00
										 |  |  |             $success = $this->_conn->quit(); | 
					
						
							| 
									
										
										
										
											2019-08-15 16:17:49 +01:00
										 |  |  |             $this->_conn = null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Ensure that a connection exists | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Checks the instance $_conn variable and connects | 
					
						
							|  |  |  |      * if it is empty. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return void | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     private function _ensureConn(): void | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (empty($this->_conn)) { | 
					
						
							|  |  |  |             $this->_conn = new Memcached(common_config('site', 'nickname')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!count($this->_conn->getServerList())) { | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |                 if (is_array($this->servers)) { | 
					
						
							|  |  |  |                     $servers = $this->servers; | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |                 } else { | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |                     $servers = [$this->servers]; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 foreach ($servers as $server) { | 
					
						
							|  |  |  |                     if (is_array($server) && count($server) === 2) { | 
					
						
							|  |  |  |                         list($host, $port) = $server; | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         $host = is_array($server) ? $server[0] : $server; | 
					
						
							|  |  |  |                         $port = 11211; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     $this->_conn->addServer($host, $port); | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |                 // Compress items stored in the cache.
 | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |                 // Allows the cache to store objects larger than 1MB (if they
 | 
					
						
							|  |  |  |                 // compress to less than 1MB), and improves cache memory efficiency.
 | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:33 +01:00
										 |  |  |                 $this->_conn->setOption(Memcached::OPT_COMPRESSION, true); | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Translate general flags to Memcached-specific flags | 
					
						
							|  |  |  |      * @param int $flag | 
					
						
							|  |  |  |      * @return int | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     protected function flag(int $flag): int | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-08-15 16:17:49 +01:00
										 |  |  |         $out = 0; | 
					
						
							|  |  |  |         if ($flag & Cache::COMPRESSED == Cache::COMPRESSED) { | 
					
						
							|  |  |  |             $out |= Memcached::OPT_COMPRESSION; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return $out; | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |     public function onPluginVersion(array &$versions): bool | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |     { | 
					
						
							|  |  |  |         $versions[] = array('name' => 'Memcached', | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |             'version' => self::PLUGIN_VERSION, | 
					
						
							|  |  |  |             'author' => 'Evan Prodromou, Craig Andrews', | 
					
						
							| 
									
										
										
										
											2019-11-21 00:21:22 +00:00
										 |  |  |             'homepage' => GNUSOCIAL_ENGINE_REPO_URL . 'tree/master/plugins/Memcached', | 
					
						
							| 
									
										
										
										
											2019-08-12 15:03:30 +01:00
										 |  |  |             'rawdescription' => | 
					
						
							|  |  |  |             // TRANS: Plugin description.
 | 
					
						
							|  |  |  |                 _m('Use <a href="http://memcached.org/">Memcached</a> to cache query results.')); | 
					
						
							| 
									
										
										
										
											2010-03-04 18:24:32 -05:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |