3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
20 namespace Wikimedia\ObjectCache
;
25 * Store data on memcached servers(s) via a pure-PHP memcached client.
27 * In configuration, the CACHE_MEMCACHED will activate the MemcachedPhpBagOStuff
28 * class. This works out of the box without any PHP extension or other PECL
29 * dependencies. If you can install the php-memcached PECL extension,
30 * it is recommended to use MemcachedPeclBagOStuff instead.
34 class MemcachedPhpBagOStuff
extends MemcachedBagOStuff
{
35 /** @var MemcachedClient */
39 * Available parameters are:
40 * - servers: The list of IP:port combinations holding the memcached servers.
41 * - persistent: Whether to use a persistent connection
42 * - compress_threshold: The minimum size an object must be before it is compressed
43 * - timeout: The read timeout in microseconds
44 * - connect_timeout: The connect timeout in seconds
46 * @param array $params
48 public function __construct( $params ) {
49 parent
::__construct( $params );
51 // Default class-specific parameters
53 'compress_threshold' => 1500,
54 'connect_timeout' => 0.5,
58 $this->client
= new MemcachedClient( $params );
59 $this->client
->set_servers( $params['servers'] );
60 $this->client
->set_debug( true );
63 protected function doGet( $key, $flags = 0, &$casToken = null ) {
64 $getToken = ( $casToken === self
::PASS_BY_REF
);
67 $routeKey = $this->validateKeyAndPrependRoute( $key );
69 // T257003: only require "gets" (instead of "get") when a CAS token is needed
70 $res = $getToken // @phan-suppress-next-line PhanTypeMismatchArgument False positive
71 ?
$this->client
->get( $routeKey, $casToken ) : $this->client
->get( $routeKey );
73 if ( $this->client
->_last_cmd_status
!== self
::ERR_NONE
) {
74 $this->setLastError( $this->client
->_last_cmd_status
);
80 protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
81 $routeKey = $this->validateKeyAndPrependRoute( $key );
83 $res = $this->client
->set( $routeKey, $value, $this->fixExpiry( $exptime ) );
85 if ( $this->client
->_last_cmd_status
!== self
::ERR_NONE
) {
86 $this->setLastError( $this->client
->_last_cmd_status
);
92 protected function doDelete( $key, $flags = 0 ) {
93 $routeKey = $this->validateKeyAndPrependRoute( $key );
95 $res = $this->client
->delete( $routeKey );
97 if ( $this->client
->_last_cmd_status
!== self
::ERR_NONE
) {
98 $this->setLastError( $this->client
->_last_cmd_status
);
104 protected function doAdd( $key, $value, $exptime = 0, $flags = 0 ) {
105 $routeKey = $this->validateKeyAndPrependRoute( $key );
107 $res = $this->client
->add( $routeKey, $value, $this->fixExpiry( $exptime ) );
109 if ( $this->client
->_last_cmd_status
!== self
::ERR_NONE
) {
110 $this->setLastError( $this->client
->_last_cmd_status
);
116 protected function doCas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
117 $routeKey = $this->validateKeyAndPrependRoute( $key );
119 $res = $this->client
->cas( $casToken, $routeKey, $value, $this->fixExpiry( $exptime ) );
121 if ( $this->client
->_last_cmd_status
!== self
::ERR_NONE
) {
122 $this->setLastError( $this->client
->_last_cmd_status
);
128 protected function doIncrWithInitAsync( $key, $exptime, $step, $init ) {
129 $routeKey = $this->validateKeyAndPrependRoute( $key );
130 $watchPoint = $this->watchErrors();
131 $this->client
->add( $routeKey, $init - $step, $this->fixExpiry( $exptime ) );
132 $this->client
->incr( $routeKey, $step );
134 return !$this->getLastError( $watchPoint );
137 protected function doIncrWithInitSync( $key, $exptime, $step, $init ) {
138 $routeKey = $this->validateKeyAndPrependRoute( $key );
140 $watchPoint = $this->watchErrors();
141 $newValue = $this->client
->incr( $routeKey, $step ) ??
false;
142 if ( $newValue === false && !$this->getLastError( $watchPoint ) ) {
143 // No key set; initialize
144 $success = $this->client
->add( $routeKey, $init, $this->fixExpiry( $exptime ) );
145 $newValue = $success ?
$init : false;
146 if ( $newValue === false && !$this->getLastError( $watchPoint ) ) {
147 // Raced out initializing; increment
148 $newValue = $this->client
->incr( $routeKey, $step ) ??
false;
155 protected function doChangeTTL( $key, $exptime, $flags ) {
156 $routeKey = $this->validateKeyAndPrependRoute( $key );
158 $res = $this->client
->touch( $routeKey, $this->fixExpiry( $exptime ) );
160 if ( $this->client
->_last_cmd_status
!== self
::ERR_NONE
) {
161 $this->setLastError( $this->client
->_last_cmd_status
);
167 protected function doGetMulti( array $keys, $flags = 0 ) {
169 foreach ( $keys as $key ) {
170 $routeKeys[] = $this->validateKeyAndPrependRoute( $key );
173 $resByRouteKey = $this->client
->get_multi( $routeKeys );
176 foreach ( $resByRouteKey as $routeKey => $value ) {
177 $res[$this->stripRouteFromKey( $routeKey )] = $value;
180 if ( $this->client
->_last_cmd_status
!== self
::ERR_NONE
) {
181 $this->setLastError( $this->client
->_last_cmd_status
);
187 protected function serialize( $value ) {
188 return is_int( $value ) ?
$value : $this->client
->serialize( $value );
191 protected function unserialize( $value ) {
192 return $this->isInteger( $value ) ?
(int)$value : $this->client
->unserialize( $value );
196 /** @deprecated class alias since 1.43 */
197 class_alias( MemcachedPhpBagOStuff
::class, 'MemcachedPhpBagOStuff' );