Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 69
0.00% covered (danger)
0.00%
0 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
MemcachedPhpBagOStuff
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 12
870
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 doGet
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 doSet
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 doDelete
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 doAdd
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 doCas
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 doIncrWithInitAsync
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 doIncrWithInitSync
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
42
 doChangeTTL
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 doGetMulti
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 serialize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 unserialize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Object caching using memcached.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Cache
22 */
23
24namespace Wikimedia\ObjectCache;
25
26use MemcachedClient;
27
28/**
29 * A wrapper class for the pure-PHP memcached client, exposing a BagOStuff interface.
30 *
31 * @ingroup Cache
32 */
33class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
34    /** @var MemcachedClient */
35    protected $client;
36
37    /**
38     * Available parameters are:
39     *   - servers:             The list of IP:port combinations holding the memcached servers.
40     *   - persistent:          Whether to use a persistent connection
41     *   - compress_threshold:  The minimum size an object must be before it is compressed
42     *   - timeout:             The read timeout in microseconds
43     *   - connect_timeout:     The connect timeout in seconds
44     *
45     * @param array $params
46     */
47    public function __construct( $params ) {
48        parent::__construct( $params );
49
50        // Default class-specific parameters
51        $params += [
52            'compress_threshold' => 1500,
53            'connect_timeout' => 0.5,
54            'timeout' => 500000,
55        ];
56
57        $this->client = new MemcachedClient( $params );
58        $this->client->set_servers( $params['servers'] );
59        $this->client->set_debug( true );
60    }
61
62    protected function doGet( $key, $flags = 0, &$casToken = null ) {
63        $getToken = ( $casToken === self::PASS_BY_REF );
64        $casToken = null;
65
66        $routeKey = $this->validateKeyAndPrependRoute( $key );
67
68        // T257003: only require "gets" (instead of "get") when a CAS token is needed
69        $res = $getToken // @phan-suppress-next-line PhanTypeMismatchArgument False positive
70            ? $this->client->get( $routeKey, $casToken ) : $this->client->get( $routeKey );
71
72        if ( $this->client->_last_cmd_status !== self::ERR_NONE ) {
73            $this->setLastError( $this->client->_last_cmd_status );
74        }
75
76        return $res;
77    }
78
79    protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
80        $routeKey = $this->validateKeyAndPrependRoute( $key );
81
82        $res = $this->client->set( $routeKey, $value, $this->fixExpiry( $exptime ) );
83
84        if ( $this->client->_last_cmd_status !== self::ERR_NONE ) {
85            $this->setLastError( $this->client->_last_cmd_status );
86        }
87
88        return $res;
89    }
90
91    protected function doDelete( $key, $flags = 0 ) {
92        $routeKey = $this->validateKeyAndPrependRoute( $key );
93
94        $res = $this->client->delete( $routeKey );
95
96        if ( $this->client->_last_cmd_status !== self::ERR_NONE ) {
97            $this->setLastError( $this->client->_last_cmd_status );
98        }
99
100        return $res;
101    }
102
103    protected function doAdd( $key, $value, $exptime = 0, $flags = 0 ) {
104        $routeKey = $this->validateKeyAndPrependRoute( $key );
105
106        $res = $this->client->add( $routeKey, $value, $this->fixExpiry( $exptime ) );
107
108        if ( $this->client->_last_cmd_status !== self::ERR_NONE ) {
109            $this->setLastError( $this->client->_last_cmd_status );
110        }
111
112        return $res;
113    }
114
115    protected function doCas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
116        $routeKey = $this->validateKeyAndPrependRoute( $key );
117
118        $res = $this->client->cas( $casToken, $routeKey, $value, $this->fixExpiry( $exptime ) );
119
120        if ( $this->client->_last_cmd_status !== self::ERR_NONE ) {
121            $this->setLastError( $this->client->_last_cmd_status );
122        }
123
124        return $res;
125    }
126
127    protected function doIncrWithInitAsync( $key, $exptime, $step, $init ) {
128        $routeKey = $this->validateKeyAndPrependRoute( $key );
129        $watchPoint = $this->watchErrors();
130        $this->client->add( $routeKey, $init - $step, $this->fixExpiry( $exptime ) );
131        $this->client->incr( $routeKey, $step );
132
133        return !$this->getLastError( $watchPoint );
134    }
135
136    protected function doIncrWithInitSync( $key, $exptime, $step, $init ) {
137        $routeKey = $this->validateKeyAndPrependRoute( $key );
138
139        $watchPoint = $this->watchErrors();
140        $newValue = $this->client->incr( $routeKey, $step ) ?? false;
141        if ( $newValue === false && !$this->getLastError( $watchPoint ) ) {
142            // No key set; initialize
143            $success = $this->client->add( $routeKey, $init, $this->fixExpiry( $exptime ) );
144            $newValue = $success ? $init : false;
145            if ( $newValue === false && !$this->getLastError( $watchPoint ) ) {
146                // Raced out initializing; increment
147                $newValue = $this->client->incr( $routeKey, $step ) ?? false;
148            }
149        }
150
151        return $newValue;
152    }
153
154    protected function doChangeTTL( $key, $exptime, $flags ) {
155        $routeKey = $this->validateKeyAndPrependRoute( $key );
156
157        $res = $this->client->touch( $routeKey, $this->fixExpiry( $exptime ) );
158
159        if ( $this->client->_last_cmd_status !== self::ERR_NONE ) {
160            $this->setLastError( $this->client->_last_cmd_status );
161        }
162
163        return $res;
164    }
165
166    protected function doGetMulti( array $keys, $flags = 0 ) {
167        $routeKeys = [];
168        foreach ( $keys as $key ) {
169            $routeKeys[] = $this->validateKeyAndPrependRoute( $key );
170        }
171
172        $resByRouteKey = $this->client->get_multi( $routeKeys );
173
174        $res = [];
175        foreach ( $resByRouteKey as $routeKey => $value ) {
176            $res[$this->stripRouteFromKey( $routeKey )] = $value;
177        }
178
179        if ( $this->client->_last_cmd_status !== self::ERR_NONE ) {
180            $this->setLastError( $this->client->_last_cmd_status );
181        }
182
183        return $res;
184    }
185
186    protected function serialize( $value ) {
187        return is_int( $value ) ? $value : $this->client->serialize( $value );
188    }
189
190    protected function unserialize( $value ) {
191        return $this->isInteger( $value ) ? (int)$value : $this->client->unserialize( $value );
192    }
193}
194
195/** @deprecated class alias since 1.43 */
196class_alias( MemcachedPhpBagOStuff::class, 'MemcachedPhpBagOStuff' );