| <?php |
| /** |
| * Copyright 2007 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /** |
| * Implementation of the interface for the "memcached" PHP extension (see |
| * http://php.net/manual/en/book.memcached.php) using the App Engine memcache |
| * API). |
| * |
| * Methods that deal with adding/removing/list of memcache servers are no-ops. |
| * |
| */ |
| |
| use google\appengine\MemcacheDeleteRequest; |
| use google\appengine\MemcacheDeleteResponse; |
| use google\appengine\MemcacheDeleteResponse\DeleteStatusCode; |
| use google\appengine\MemcacheGetRequest; |
| use google\appengine\MemcacheGetResponse; |
| use google\appengine\MemcacheIncrementRequest; |
| use google\appengine\MemcacheIncrementResponse; |
| use google\appengine\MemcacheSetRequest; |
| use google\appengine\MemcacheSetRequest\SetPolicy; |
| use google\appengine\MemcacheSetResponse; |
| use google\appengine\MemcacheSetResponse\SetStatusCode; |
| use google\appengine\runtime\ApiProxy; |
| use google\appengine\runtime\Error; |
| use google\appengine\runtime\MemcacheUtils; |
| |
| class Memcached { |
| |
| /** |
| * Constants taken from http://www.php.net/manual/en/memcached.constants.php |
| */ |
| const OPT_PREFIX_KEY = 1; |
| const OPT_COMPRESSION = 2; |
| const OPT_HASH = 3; |
| const OPT_DISTRIBUTION = 4; |
| const OPT_BUFFER_WRITES = 5; |
| const OPT_BINARY_PROTOCOL = 6; |
| const OPT_NO_BLOCK = 7; |
| const OPT_TCP_NODELAY = 8; |
| const OPT_SOCKET_SEND_SIZE = 9; |
| const OPT_SOCKET_RECV_SIZE = 10; |
| const OPT_CONNECT_TIMEOUT = 11; |
| const OPT_RETRY_TIMEOUT = 12; |
| const OPT_SEND_TIMEOUT = 13; |
| const OPT_RECV_TIMEOUT = 14; |
| const OPT_POLL_TIMEOUT = 15; |
| const OPT_CACHE_LOOKUPS = 16; |
| const OPT_SERVER_FAILURE_LIMIT = 17; |
| const OPT_SERIALIZER = 18; |
| const HAVE_IGBINARY = 20; |
| const HAVE_JSON = 21; |
| |
| // SERIALIZER constants |
| const SERIALIZER_PHP = 100; |
| const SERIALIZER_IGBINARY = 101; |
| const SERIALIZER_JSON = 102; |
| |
| // HASH constants |
| const HASH_DEFAULT = 200; |
| const HASH_MD5 = 201; |
| const HASH_CRC = 202; |
| const HASH_FNV1_64 = 203; |
| const HASH_FNV1A_64 = 204; |
| const HASH_FNV1_32 = 205; |
| const HASH_FNV1A_32 = 206; |
| const HASH_HSIEH = 207; |
| const HASH_MURMUR = 208; |
| |
| // DISTRIBUTION constants |
| const DISTRIBUTION_MODULA = 300; |
| const DISTRIBUTION_CONSISTENT = 301; |
| const OPT_LIBKETAMA_COMPATIBLE = 302; |
| |
| const GET_PRESERVE_ORDER = 10; |
| |
| const RES_SUCCESS = 0; |
| const RES_FAILURE = 1; |
| const RES_HOST_LOOKUP_FAILURE = 2; |
| const RES_UNKNOWN_READ_FAILURE = 7; |
| const RES_PROTOCOL_ERROR = 8; |
| const RES_CLIENT_ERROR = 9; |
| const RES_SERVER_ERROR = 10; |
| const RES_WRITE_FAILURE = 5; |
| const RES_DATA_EXISTS = 12; |
| const RES_NOTSTORED = 14; |
| const RES_NOTFOUND = 16; |
| const RES_PARTIAL_READ = 18; |
| const RES_SOME_ERRORS = 19; |
| const RES_NO_SERVERS = 20; |
| const RES_END = 21; |
| const RES_ERRNO = 26; |
| const RES_BUFFERED = 32; |
| const RES_TIMEOUT = 31; |
| const RES_BAD_KEY_PROVIDED = 33; |
| const RES_CONNECTION_SOCKET_CREATE_FAILURE = 11; |
| const RES_PAYLOAD_FAILURE = -1001; |
| |
| private $result_code; |
| private $memcache; |
| private $options; |
| private $delayed_results; |
| |
| public function __construct($persistent_id = null) { |
| $this->result_code = self::RES_SUCCESS; |
| $this->memcache = new Memcache(); |
| $this->options = [self::OPT_COMPRESSION => false]; |
| $this->delayed_results = array(); |
| } |
| |
| /** |
| * add is similar to set(), but the operation fails if the key already exists |
| * on the server. |
| * |
| * @see Memcached::set() |
| * |
| * @param string $key The key under which to store the value. |
| * @param mixed $value The value to store. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return bool true on success, false on failure. |
| */ |
| public function add($key, $value, $expiration = 0) { |
| $key = $this->getPrefixKey($key); |
| $result = $this->memcache->add($key, $value, null, $expiration); |
| $this->result_code = $result ? self::RES_SUCCESS : self::RES_NOTSTORED; |
| return $result; |
| } |
| |
| /** |
| * Memcached::addByKey() is functionally equivalent to Memcached::add(), |
| * except that the free-form server_key can be used to map the key to a |
| * specific server. This is useful if you need to keep a bunch of related keys |
| * on a certain server. |
| * |
| * @see Memcached::add() |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param string $key The key under which to store the value. |
| * @param mixed $value The value to store. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return bool true on success, false on failure. |
| */ |
| public function addByKey($server_key, |
| $key, |
| $value, |
| $expiration = 0) { |
| return $this->add($key, $value, $expiration); |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function addServer($host, $port, $weight = 0) { |
| return true; |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function addServers($servers) { |
| return true; |
| } |
| |
| /** |
| * Memcached::append() appends the given value string to the value of an |
| * existing item. The reason that value is forced to be a string is that |
| * appending mixed types is not well-defined. |
| * |
| * @param string $key The key under which to append the value. |
| * @param string $value The value to append |
| * |
| * @result bool Returns true on success or false on failure. |
| */ |
| public function append($key, $value) { |
| do { |
| $result = $this->get($key, null, $cas_token); |
| if (!$result || !is_string($result)) { |
| $this->result_code = self::RES_NOTSTORED; |
| return false; |
| } |
| |
| $result = $result . $value; |
| $result = $this->cas($cas_token, $key, $result); |
| } while (!$result && $this->result_code == self::RES_DATA_EXISTS); |
| $this->result_code = $result ? self::RES_SUCCESS : self::RES_NOTSTORED; |
| return $result; |
| } |
| |
| /** |
| * @see Memcached::append(). |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param string $key The key under which to append the value. |
| * @param string $value The value to append |
| * |
| * @result bool Returns true on success or false on failure. |
| */ |
| public function appendByKey(string $server_key, string $key, string $value) { |
| return $this->append($key, $value); |
| } |
| |
| /** |
| * Performs a set and check operation, so that the item will be stored only |
| * if no other client has updated it since it was last fetched by this |
| * client. |
| * |
| * @param mixed $cas_token Unique memcached assigned value. |
| * @param string $key The key under which to store the value. |
| * @param mixed $value The value to store. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return bool True on success, or false on failure. |
| */ |
| public function cas($cas_token, $key, $value, $expiration = 0) { |
| $key = $this->getPrefixKey($key); |
| $request = new MemcacheSetRequest(); |
| $response = new MemcacheSetResponse(); |
| |
| $memcache_flag = 0; |
| $serialized_value = MemcacheUtils::serializeValue($value, $memcache_flag); |
| |
| $item = $request->addItem(); |
| $item->setKey($key); |
| $item->setValue($serialized_value); |
| $item->setFlags($memcache_flag); |
| $item->setSetPolicy(SetPolicy::CAS); |
| $item->setCasId($cas_token); |
| $item->setExpirationTime($expiration); |
| |
| try { |
| ApiProxy::makeSyncCall('memcache', 'Set', $request, $response); |
| } catch (Error $e) { |
| $this->result_code = self::RES_FAILURE; |
| return false; |
| } |
| |
| switch ($response->getSetStatusList()[0]) { |
| case SetStatusCode::STORED: |
| $this->result_code = self::RES_SUCCESS; |
| return true; |
| case SetStatusCode::NOT_STORED: |
| $this->result_code = self::RES_NOTSTORED; |
| return false; |
| case SetStatusCode::EXISTS: |
| $this->result_code = self::RES_DATA_EXISTS; |
| return false; |
| default: |
| $this->result_code = self::RES_FAILURE; |
| return false; |
| } |
| } |
| |
| /** |
| * @see Memcached::cas(). |
| * |
| * @param mixed $cas_token Unique memcached assigned value. |
| * @param string $server_key Ignored. |
| * @param string $key The key under which to store the value. |
| * @param mixed $value The value to store. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return bool True on success, or false on failure. |
| */ |
| public function casByKey($cas_token, |
| $server_key, |
| $key, |
| $value, |
| $expiration = 0) { |
| return cas($cas_token, $key, $value, $expiration); |
| } |
| |
| /** |
| * Decrements a numeric item's value by $offset. |
| * |
| * @param string $key The key under which to store the value. |
| * @param int $offset The amount by which to decrement the item's value. |
| * @param int $initial_value The value to set the item to if it does not |
| * currently exist. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return bool True on success, or false on failure. |
| */ |
| public function decrement($key, |
| $offset = 1, |
| $initial_value = 0, |
| $expiration = 0) { |
| return $this->increment($key, -$offset, $initial_value, $expiration); |
| } |
| |
| /** |
| * @see Memcached::decrement(). |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param string $key The key under which to store the value. |
| * @param int $offset The amount by which to decrement the item's value. |
| * @param int $initial_value The value to set the item to if it does not |
| * currently exist. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return bool True on success, or false on failure. |
| */ |
| public function decrementByKey($server_key, |
| $key, |
| $offset = 1, |
| $initial_value = 0, |
| $expiration = 0) { |
| return decrement($key, $offset, $initial_value, $expiration); |
| } |
| |
| /** |
| * deletes the $key from the server. |
| * |
| * @param string $key The key to delete from the server. |
| * @param int $time The time parameter is the amount of time in seconds the |
| * client wishes the server to refuse add and replace commands for this key. |
| * |
| * @return bool true on success or false on failure. |
| */ |
| public function delete($key, $time = 0) { |
| return $this->deleteMulti([$key], $time); |
| } |
| |
| /** |
| * @see Memcached::delete(). |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param string $key The key to delete from the server. |
| * @param int $time The time parameter is the amount of time in seconds the |
| * client wishes the server to refuse add and replace commands for this key. |
| * |
| * @return bool true on success or false on failure. |
| */ |
| public function deleteByKey($server_key, $key, $time = 0) { |
| return $this->delete($key, $time); |
| } |
| |
| /** |
| * deletes an array of $keys from the server. |
| * |
| * @param array $keys The keys to delete from the server. |
| * @param int $time The time parameter is the amount of time in seconds the |
| * client wishes the server to refuse add and replace commands for this key. |
| * |
| * @return bool true on success or false on failure. |
| */ |
| public function deleteMulti($keys, $time = 0) { |
| $request = new MemcacheDeleteRequest(); |
| $response = new MemcacheDeleteResponse(); |
| |
| foreach($keys as $key) { |
| $key = $this->getPrefixKey($key); |
| $item = $request->addItem(); |
| $item->setKey($key); |
| $item->setDeleteTime($time); |
| } |
| |
| try { |
| ApiProxy::makeSyncCall('memcache', 'Delete', $request, $response); |
| } catch (Error $e) { |
| $this->result_code = self::RES_FAILURE; |
| return false; |
| } |
| |
| foreach($response->getDeleteStatusList() as $status) { |
| if ($status == DeleteStatusCode::NOT_FOUND) { |
| $this->result_code = self::RES_NOTFOUND; |
| return false; |
| } |
| } |
| |
| $this->result_code = self::RES_SUCCESS; |
| return true; |
| } |
| |
| /** |
| * @see Memcache::deleteMulti(). |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param array $keys The keys to delete from the server. |
| * @param int $time The time parameter is the amount of time in seconds the |
| * client wishes the server to refuse add and replace commands for this key. |
| * |
| * @return bool true on success or false on failure. |
| */ |
| public function deleteMultiByKey($server_key, $keys, $time = 0) { |
| return $this->deleteMulti($keys, $time); |
| } |
| |
| /** |
| * fetch retrieves the next result from the last getDelayed() request. |
| * |
| * Note that currently getDelayed is a synchronous call. |
| * |
| * @return The next result, or false if there are no more results. |
| */ |
| public function fetch() { |
| if (!empty($this->delayed_results)) { |
| return array_shift($this->delayed_results); |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Fetch all of the remaining results from the last getDelayed() request. |
| * |
| * Note that currently getDelayed is a synchronous call. |
| * |
| * @return array The remaining results, or false if there are no results. |
| */ |
| public function fetchAll() { |
| if (!empty($this->delayed_results)) { |
| $result = $this->delayed_results; |
| $this->delayed_results = array(); |
| return $result; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Invalidates all existing cache items immediately. |
| * |
| * @param int $delay This parameter is ignored. |
| * |
| * @return bool true on success, or false on failure. |
| */ |
| public function flush($delay = 0) { |
| $result = $this->memcache->flush(); |
| $this->result_code = $result ? self::RES_SUCCESS : self::RES_NOTSTORED; |
| return $result; |
| } |
| |
| /** |
| * Returns the item that was previously stored under the $key. |
| * |
| * @param string $key The key under which to store the value. |
| * @param callable $cache_cb Read through caching callback. |
| * @param mixed $cas_token The variable to store the CAS token in. This value |
| * is opaque to the application. |
| * |
| * @return the value stored in the cache of false if there was a failure. |
| */ |
| public function get($key, $cache_cb = null, &$cas_token = null) { |
| // Not re-using getMulti to avoid messing with multiple result arrays for |
| // cas tokens. |
| $request = new MemcacheGetRequest(); |
| $response = new MemcacheGetResponse(); |
| |
| $key = $this->getPrefixKey($key); |
| $request->addKey($key); |
| // Only way to check if we were passed a $cas_token is checking the number |
| // of passed in arguments. |
| if (func_num_args() == 3) { |
| $request->setForCas(true); |
| } |
| |
| try { |
| ApiProxy::makeSyncCall('memcache', 'Get', $request, $response); |
| } catch (Error $e) { |
| $this->result_code = self::RES_FAILURE; |
| return false; |
| } |
| |
| $result = $response->getItemList(); |
| // If the get failed, and if a read through cache callback has been set |
| // then call it now. $result is pass-by-ref and will contain the new value. |
| if (empty($result) && !is_null($cache_cb) && is_null($cas_token)) { |
| $cb_result = $cache_cb($this, $key, $new_result); |
| if ($cb_result) { |
| // TODO: What to do if this set fails? |
| $this->memcache->set($key, $new_result); |
| $this->result_code = self::RES_SUCCESS; |
| return $new_result; |
| } else { |
| $this->result_code = self::RES_FAILURE; |
| return false; |
| } |
| } else if (!empty($result)) { |
| $item = $result[0]; |
| if ($item->hasCasId()) { |
| $cas_token = $item->getCasId(); |
| } |
| $this->result_code = self::RES_SUCCESS; |
| try { |
| return MemcacheUtils::deserializeValue($item->getValue(), |
| $item->getFlags()); |
| } catch (\UnexpectedValueException $e) { |
| $this->result_code = self::RES_NOTFOUND; |
| return false; |
| } |
| } else { |
| $this->result_code = self::RES_NOTFOUND; |
| return false; |
| } |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function getAllKeys() { |
| return array(); |
| } |
| |
| /** |
| * @see Memcache::get(). |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param string $key The key under which to store the value. |
| * @param callable $cache_cb Read through caching callback. |
| * @param mixed $cas_token The variable to store the CAS token in. This value |
| * is opaque to the application. |
| * |
| * @return the value stored in the cache of false if there was a failure. |
| */ |
| public function getByKey($server_key, $key, $cache_cb, &$cas_token) { |
| return $this->get($key, $cache_cb, $cas_token); |
| } |
| |
| /** |
| * Issues a request to memcache for multiple items the keys of which are |
| * specified in the keys array. |
| * Currently this method executes synchronously. |
| * |
| * @param array $keys Array of keys to retrieve. |
| * @param bool $with_cas If true, retrieve the CAS tokens for the keys. |
| * @param callable $value_cb The result callback. |
| * |
| * @return bool true on success, or false on failure. |
| */ |
| public function getDelayed($keys, $with_cas=false, $value_cb=null) { |
| // Clear any previous delayed results. |
| $this->delayed_results = array(); |
| |
| $cas_tokens = null; |
| if ($with_cas) { |
| $results = $this->getMulti($keys, $cas_tokens); |
| } else { |
| $results = $this->getMulti($keys); |
| } |
| |
| if (!$results) { |
| return false; |
| } |
| |
| foreach($results as $key => $value) { |
| $val = ['key' => $key, 'value' => $value]; |
| if (!empty($cas_tokens)) { |
| $cas = array_shift($cas_tokens); |
| $val['cas'] = $cas; |
| } |
| $this->delayed_results[] = $val; |
| } |
| |
| if (isset($value_cb)) { |
| foreach($this->delayed_results as $result) { |
| $value_cb($result); |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * @see getDelayedByKey. |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param array $keys Array of keys to retrieve. |
| * @param bool $with_cas If true, retrieve the CAS tokens for the keys. |
| * @param callable $value_cb The result callback. |
| * |
| * @return bool true on success, or false on failure. |
| */ |
| public function getDelayedByKey($server_key, |
| $keys, |
| $with_cas = false, |
| $value_cb = null) { |
| return $this->getDelayed($keys, $with_cas, $value_cb); |
| } |
| |
| /** |
| * Similar to Memcached::get(), but instead of a single key item, it retrieves |
| * multiple items the keys of which are specified in the keys array. |
| * |
| * @see Memcached::get() |
| * |
| * @param array $keys Array of keys to retrieve. |
| * @param array $cas_tokens The variable to store the CAS tokens for found |
| * items. |
| * @param int $flags The flags for the get operation. |
| * |
| * @return array The array of found items for false on failure. |
| */ |
| public function getMulti($keys, &$cas_tokens = null, $flags = 0) { |
| $request = new MemcacheGetRequest(); |
| $response = new MemcacheGetResponse(); |
| |
| foreach ($keys as $key) { |
| $key = $this->getPrefixKey($key); |
| $request->addKey($key); |
| } |
| |
| // Need to check the number of arguments passed to the function to see if |
| // the user wants cas_tokens. |
| if (func_num_args() > 1) { |
| $request->setForCas(true); |
| } |
| |
| try { |
| ApiProxy::makeSyncCall('memcache', 'Get', $request, $response); |
| } catch (Error $e) { |
| $this->result_code = self::RES_FAILURE; |
| return false; |
| } |
| |
| $return_value = array(); |
| foreach ($response->getItemList() as $item) { |
| try { |
| $return_value[$item->getKey()] = MemcacheUtils::deserializeValue( |
| $item->getValue(), $item->getFlags()); |
| } catch (\UnexpectedValueException $e) { |
| // Skip entries that cannot be deserialized. |
| continue; |
| } |
| if ($item->hasCasId()) { |
| $cas_tokens[$item->getKey()] = $item->getCasId(); |
| } |
| } |
| // If GET_PRESERVE_ORDER was set then we need to ensure that |
| // a. Keys are returned in the order that they we asked for. |
| // b. If a key has no value then return null for that key. |
| if ($flags == self::GET_PRESERVE_ORDER) { |
| $ordered_result = []; |
| $ordered_cas_tokens = []; |
| foreach ($keys as $key) { |
| if (array_key_exists($key, $return_value)) { |
| $ordered_result[$key] = $return_value[$key]; |
| if (array_key_exists($key, $cas_tokens)) { |
| $ordered_cas_tokens[$key] = $cas_tokens[$key]; |
| } else { |
| $ordered_cas_tokens[$key] = null; |
| } |
| } else { |
| $ordered_result[$key] = null; |
| $ordered_cas_tokens[$key] = null; |
| } |
| } |
| $return_value = $ordered_result; |
| if (func_num_args() > 1) { |
| $cas_tokens = $ordered_cas_tokens; |
| } |
| } |
| return $return_value; |
| } |
| |
| /** |
| * @see Memcached::getMulti() |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param array $keys Array of keys to retrieve. |
| * @param array $cas_tokens The variable to store the CAS tokens for found |
| * items. |
| * @param int $flags The flags for the get operation. |
| * |
| * @return array The array of found items for false on failure. |
| */ |
| public function getMultiByKey($server_key, |
| $keys, |
| $with_cas = false, |
| $value_cb = null) { |
| return $this->getMulti($keys, $with_cas, $value_cb); |
| } |
| |
| /** |
| * Retrieve a Memcached option value. |
| * |
| * @params int $option One of the Memcached::OPT_* constants. |
| * |
| * @return mixed the value of the requested option, of false on error. |
| */ |
| public function getOption($option) { |
| if (array_key_exists($option, $this->options)) { |
| return $this->options[$option]; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns one of the Memcached::RES_* constants that is the result of the |
| * last executed Memcached method. |
| * |
| * @return int The result code of the last memcached operation. |
| */ |
| public function getResultCode() { |
| return $this->result_code; |
| } |
| |
| /** |
| * Return the message describing the result of the last operation. |
| * |
| * @return string Message describing the result of the last operation. |
| */ |
| public function getResultMessage() { |
| // We're only handling the results that our code actually generates. |
| switch ($this->result_code) { |
| case self::RES_SUCCESS: |
| return "SUCCESS"; |
| case self::RES_FAILURE: |
| return "FAILURE"; |
| case self::RES_NOTSTORED: |
| return "NOT STORED"; |
| case self::RES_NOT_FOUND: |
| return "NOT FOUND"; |
| } |
| return "UNKNOWN"; |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function getServerByKey($server_key) { |
| return false; |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function getServerList() { |
| return []; |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function getStats() { |
| return []; |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function getVersion() { |
| return array(); |
| } |
| |
| /** |
| * Increments a numeric item's value by the specified offset. If the item's |
| * value is not numeric, and error will result. |
| * |
| * @param string $key The key of the item to increment |
| * @param int $offset The amount by which to increment the item's value |
| * @param int $initial_value The value to set the item to if it doesn't exist. |
| * @param int $expiry The expiry time to set on the item. |
| * |
| * @return The new item's value on success or false on failure. |
| */ |
| public function increment($key, |
| $offset = 1, |
| $initial_value = 0, |
| $expiry = 0) { |
| // Sending of a key of 'null' or an unset value is a failure. |
| if (is_null($key)) { |
| return false; |
| } |
| |
| $key = $this->getPrefixKey($key); |
| $request = new MemcacheIncrementRequest(); |
| $response = new MemcacheIncrementResponse(); |
| $request->setKey($key); |
| $request->setDelta($offset); |
| $request->setInitialValue($initial_value); |
| |
| try { |
| ApiProxy::makeSyncCall('memcache', 'Increment', $request, $response); |
| } catch (Error $e) { |
| $this->result_code = self::RES_FAILURE; |
| return false; |
| } |
| if ($response->hasNewValue()) { |
| $this->result_code = self::RES_SUCCESS; |
| return $response->getNewValue(); |
| } else { |
| $this->result_code = self::RES_NOTSTORED; |
| return false; |
| } |
| } |
| |
| /** |
| * @see Memcached::increment() |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param string $key The key of the item to increment |
| * @param int $offset The amount by which to increment the item's value |
| * @param int $initial_value The value to set the item to if it doesn't exist. |
| * @param int $expiry The expiry time to set on the item. |
| * |
| * @return The new item's value on success or false on failure. |
| */ |
| public function incrementByKey($server_key, |
| $key, |
| $offset = 1, |
| $initial_value = 0, |
| $expiry = 0) { |
| return $this->increment($key, $offset, $initial_value, $expiry); |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function isPersistent() { |
| return false; |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function isPristine() { |
| return false; |
| } |
| |
| /** |
| * Prepends the given value string to an existing item. |
| * |
| * @param string $key The key under which to store the value. |
| * @param string $value The string to prepend. |
| * |
| * @return true on success or false on failure. |
| */ |
| public function prepend($key, $value) { |
| do { |
| $result = $this->get($key, null, $cas_token); |
| if (!$result || !is_string($result)) { |
| $this->result_code = self::RES_NOTSTORED; |
| return false; |
| } |
| |
| $result = $value . $result; |
| $result = $this->cas($cas_token, $key, $result); |
| } while (!$result && $this->result_code == self::RES_DATA_EXISTS); |
| |
| $this->result_code = $result ? self::RES_SUCCESS : self::RES_NOTSTORED; |
| return $result; |
| } |
| |
| /** |
| * @see Memcached::prepend() |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param string $key The key under which to store the value. |
| * @param string $value The string to prepend. |
| * |
| * @return true on success or false on failure. |
| */ |
| public function prependByKey($server_key, $key, $value) { |
| return $this->prepend($key, $value); |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function quit() { |
| return false; |
| } |
| |
| /** |
| * Replace is similar to Memcache::set(), but the operation will fail if the |
| * key is not found on the server. |
| * |
| * @param string $key The key under which to store the value. |
| * @param mixed $value The value to store. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return true if the method succeeds, false on failure. |
| */ |
| public function replace($key, $value, $expiration = 0) { |
| $key = $this->getPrefixKey($key); |
| $result = $this->memcache->replace($key, $value, null, $expiration); |
| $this->result_code = $result ? self::RES_SUCCESS : self::RES_NOTSTORED; |
| return $result; |
| } |
| |
| /** |
| * @see Memcached::replace() |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param string $key The key under which to store the value. |
| * @param mixed $value The value to store. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return true if the method succeeds, false on failure. |
| */ |
| public function replaceByKey($server_key, $key, $value, $expiration = 0) { |
| return $this->replace($key, $value, $expiration); |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function resetServerList() { |
| return false; |
| } |
| |
| /** |
| * Stores the value on a memcache server under the specified key. The |
| * expiration parameters can be used to control when the value is considered |
| * expired. |
| * |
| * @param string $key The key under which to store the value. |
| * @param mixed $value The value to store. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return true if the method succeeds, false on failure. |
| */ |
| public function set($key, $value, $expiration = 0) { |
| $key = $this->getPrefixKey($key); |
| $result = $this->memcache->set($key, $value, null, $expiration); |
| $this->result_code = $result ? self::RES_SUCCESS : self::RES_FAILURE; |
| return $result; |
| } |
| |
| /** |
| * @see Memcached::set() |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param string $key The key under which to store the value. |
| * @param mixed $value The value to store. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return true if the method succeeds, false on failure. |
| */ |
| public function setByKey($server_key, $key, $value, $expiration = 0) { |
| return $this->set($key, $value, $expiration); |
| } |
| |
| /** |
| * Is similar to Memcached::set(), but instead of a single key/value item, it |
| * works on multiple items specified in items. |
| * |
| * @see Memcached::set() |
| * |
| * @param array $items An array of key value pairs to set. |
| * @param int $expiration The expiration time to set for the value. |
| * |
| * returns bool true if the call succeeds, false otherwise. |
| */ |
| public function setMulti($items, $expiration = 0) { |
| if (array_key_exists(self::OPT_PREFIX_KEY, $this->options)) { |
| $new_items = array(); |
| foreach($items as $key => $value) { |
| $new_items[$this->getPrefixKey($key)] = $value; |
| } |
| $items = $new_items; |
| } |
| |
| try { |
| $set_results = MemcacheUtils::setMultiWithPolicy($items, |
| $expiration, |
| SetPolicy::SET); |
| } catch (Exception $e) { |
| $this->result_code = self::RES_FAILURE; |
| return false; |
| } |
| |
| // If any fail, report this method as failed. |
| foreach($set_results as $result) { |
| if ($result != SetStatusCode::STORED) { |
| $this->result_code = self::RES_NOTSTORED; |
| return false; |
| } |
| } |
| |
| $this->result_code = self::RES_SUCCESS; |
| return true; |
| } |
| |
| /** |
| * @see Memcached::setMulti() |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param array $items An array of key value pairs to set. |
| * @param int $expiration The expiration time to set for the value. |
| * |
| * @return bool true if the call succeeds, false otherwise. |
| */ |
| public function setMultiByKey($server_key, $items, $expiration = 0) { |
| return $this->setMulti($items, $expiration); |
| } |
| |
| /** |
| * This method sets the vaue of a memcached option. |
| * |
| * @param int $option The option to set. |
| * @param mixed $value The value to set the option to. |
| * |
| * @return bool true if the call succeeds, false otherwise. |
| */ |
| public function setOption($option, $value) { |
| // The only option we allow to be changed is OPT_PREFIX_KEY |
| if ($option == self::OPT_PREFIX_KEY) { |
| $this->options[$option] = $value; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * This is a varion of Memcached::setOption() that takes an array of options |
| * to be set. |
| * |
| * @param mixed $options An associated array of options. |
| * |
| * @return bool true if the call succeeds, false otherwise. |
| */ |
| public function setOptions($options) { |
| $result = true; |
| foreach($options as $option => $value) { |
| $result |= $this->setOption($option, $value); |
| } |
| return $result; |
| } |
| |
| /** |
| * This function is present only for compatibility and does nothing. |
| */ |
| public function setSaslAuthData($username, $password) { |
| } |
| |
| /** |
| * Sets a new expiration time on an item. |
| * |
| * @param string $key The key under which to append the value. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return bool true on success or false on failure. |
| */ |
| public function touch($key, $expiration = 0) { |
| $result = $this->get($key, null, $cas_token); |
| if ($result) { |
| $result = $this->cas($cas_token, $key, $result, $expiration); |
| } |
| $this->result_code = $result ? self::RES_SUCCESS : self::RES_FAILURE; |
| return $result; |
| } |
| |
| /** |
| * Functionally equivalent to Memcached::touch(). |
| * |
| * @param string $server_key This parameter is ignored. |
| * @param string $key The key under which to append the value. |
| * @param int $expiration The expiration time, defaults to 0. |
| * |
| * @return bool true on success or false on failure. |
| */ |
| public function touchByKey($server_key, $key, $expiration = 0) { |
| return $this->touch($key, $expiration); |
| } |
| |
| private function getPrefixKey($key) { |
| if (array_key_exists(self::OPT_PREFIX_KEY, $this->options) && isset($key)) { |
| $key = $this->options[self::OPT_PREFIX_KEY] . $key; |
| } |
| return $key; |
| } |
| } |