mirror of
https://github.com/phpredis/phpredis.git
synced 2026-06-19 07:35:31 +00:00
2c5ef19257
* Introduce a new `RedisCmd` struct to dynamically append RESP arguments such that we don't have to precalculate the number of arguments the command will have up front. Additionally the new `RedisCmd allows both a `void *` context pointer but also can attach a `void (*ctx_dtor)(void*)` destructor so we are still able to clean up any allocated context when commands fail. This moves the context cleanup out of every individual reply handler and into the generic processing wrappers. * Create a small group of `resp_str` helper functions for lower level concatination of RESP protocol data over the wire. * Lots of small modernization of the codebase such as using `zend_string*` instead of (`char *`, `size_t`) pairs. * Greatly simplify `crosslot` handling logic
3326 lines
90 KiB
C
3326 lines
90 KiB
C
/* -*- Mode: C; tab-width: 4 -*- */
|
|
/*
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 1997-2009 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Original author: Alfonso Jimenez <yo@alfonsojimenez.com> |
|
|
| Maintainer: Nicolas Favre-Felix <n.favre-felix@owlient.eu> |
|
|
| Maintainer: Nasreddine Bouafif <n.bouafif@owlient.eu> |
|
|
| Maintainer: Michael Grunder <michael.grunder@gmail.com> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "php_redis.h"
|
|
#include "redis_array.h"
|
|
#include "redis_cluster.h"
|
|
#include "redis_commands.h"
|
|
#include "redis_sentinel.h"
|
|
#include <ext/spl/spl_exceptions.h>
|
|
#include <zend_exceptions.h>
|
|
#include <ext/standard/info.h>
|
|
#include <ext/hash/php_hash.h>
|
|
|
|
#if PHP_VERSION_ID < 80400
|
|
#include <ext/standard/php_random.h>
|
|
#else
|
|
#include <ext/random/php_random.h>
|
|
#endif
|
|
|
|
#ifdef PHP_SESSION
|
|
#include <ext/session/php_session.h>
|
|
#endif
|
|
|
|
#include "library.h"
|
|
|
|
#ifdef HAVE_REDIS_ZSTD
|
|
#include <zstd.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_REDIS_LZ4
|
|
#include <lz4.h>
|
|
#endif
|
|
|
|
#ifdef PHP_SESSION
|
|
extern ps_module ps_mod_redis;
|
|
extern ps_module ps_mod_redis_cluster;
|
|
#endif
|
|
|
|
zend_class_entry *redis_ce;
|
|
zend_class_entry *redis_exception_ce;
|
|
|
|
extern RedisCmdCtx redis_empty_ctx;
|
|
|
|
#if PHP_VERSION_ID < 80000
|
|
#include "redis_legacy_arginfo.h"
|
|
#else
|
|
#include "zend_attributes.h"
|
|
#include "redis_arginfo.h"
|
|
#endif
|
|
|
|
extern const zend_function_entry *redis_get_methods(void)
|
|
{
|
|
return class_Redis_methods;
|
|
}
|
|
|
|
extern int le_cluster_slot_cache;
|
|
int le_redis_pconnect;
|
|
|
|
PHP_INI_BEGIN()
|
|
/* redis arrays */
|
|
PHP_INI_ENTRY("redis.arrays.algorithm", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.auth", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.autorehash", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.connecttimeout", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.distributor", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.functions", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.hosts", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.index", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.lazyconnect", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.names", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.pconnect", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.readtimeout", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.retryinterval", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.consistent", "0", PHP_INI_ALL, NULL)
|
|
|
|
/* redis cluster */
|
|
PHP_INI_ENTRY("redis.clusters.cache_slots", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.clusters.auth", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.clusters.persistent", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.clusters.read_timeout", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.clusters.timeout", "0", PHP_INI_ALL, NULL)
|
|
|
|
/* redis pconnect */
|
|
PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "1", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.pconnect.echo_check_liveness", "1", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.pconnect.pool_detect_dirty", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.pconnect.pool_poll_timeout", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.pconnect.pool_pattern", "", PHP_INI_ALL, NULL)
|
|
|
|
/* redis session */
|
|
PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.lock_release_cmd", "eval", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.lock_retries", "100", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.lock_wait_time", "20000", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.lock_failure_readonly", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.early_refresh", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.compression", "none", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.compression_level", "3", PHP_INI_ALL, NULL)
|
|
PHP_INI_END()
|
|
|
|
static const zend_module_dep redis_deps[] = {
|
|
#ifdef HAVE_REDIS_IGBINARY
|
|
ZEND_MOD_REQUIRED("igbinary")
|
|
#endif
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
ZEND_MOD_REQUIRED("msgpack")
|
|
#endif
|
|
#ifdef HAVE_REDIS_JSON
|
|
ZEND_MOD_REQUIRED("json")
|
|
#endif
|
|
#ifdef PHP_SESSION
|
|
ZEND_MOD_REQUIRED("session")
|
|
#endif
|
|
ZEND_MOD_END
|
|
};
|
|
|
|
ZEND_DECLARE_MODULE_GLOBALS(redis)
|
|
|
|
zend_module_entry redis_module_entry = {
|
|
STANDARD_MODULE_HEADER_EX,
|
|
NULL,
|
|
redis_deps,
|
|
"redis",
|
|
NULL,
|
|
PHP_MINIT(redis),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
PHP_MINFO(redis),
|
|
PHP_REDIS_VERSION,
|
|
PHP_MODULE_GLOBALS(redis),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
STANDARD_MODULE_PROPERTIES_EX
|
|
};
|
|
|
|
#ifdef COMPILE_DL_REDIS
|
|
ZEND_GET_MODULE(redis)
|
|
#endif
|
|
|
|
zend_object_handlers redis_object_handlers;
|
|
|
|
/* Send a static DISCARD in case we're in MULTI mode. */
|
|
static int
|
|
redis_send_discard(RedisSock *redis_sock)
|
|
{
|
|
char *resp;
|
|
int resp_len, result = FAILURE;
|
|
|
|
/* send our DISCARD command */
|
|
if (redis_sock_write(redis_sock, ZEND_STRL(RESP_DISCARD_CMD)) >= 0 &&
|
|
(resp = redis_sock_read(redis_sock,&resp_len)) != NULL)
|
|
{
|
|
/* success if we get OK */
|
|
result = (resp_len == 3 && redis_strncmp(resp, ZEND_STRL("+OK")) == 0) ? SUCCESS:FAILURE;
|
|
|
|
/* free our response */
|
|
efree(resp);
|
|
}
|
|
|
|
/* return success/failure */
|
|
return result;
|
|
}
|
|
|
|
/* Passthru for destroying cluster cache */
|
|
static void cluster_cache_dtor(zend_resource *rsrc) {
|
|
if (rsrc->ptr) {
|
|
cluster_cache_free(rsrc->ptr);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_redis_object(zend_object *object)
|
|
{
|
|
redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object);
|
|
|
|
zend_object_std_dtor(&redis->std);
|
|
if (redis->sock) {
|
|
redis_sock_disconnect(redis->sock, 0, 1);
|
|
redis_free_socket(redis->sock);
|
|
}
|
|
}
|
|
|
|
zend_object *
|
|
create_redis_object(zend_class_entry *ce)
|
|
{
|
|
redis_object *redis = ecalloc(1, sizeof(redis_object) + zend_object_properties_size(ce));
|
|
|
|
redis->sock = NULL;
|
|
|
|
zend_object_std_init(&redis->std, ce);
|
|
object_properties_init(&redis->std, ce);
|
|
|
|
memcpy(&redis_object_handlers, zend_get_std_object_handlers(), sizeof(redis_object_handlers));
|
|
redis_object_handlers.offset = XtOffsetOf(redis_object, std);
|
|
redis_object_handlers.free_obj = free_redis_object;
|
|
redis_object_handlers.clone_obj = NULL;
|
|
redis->std.handlers = &redis_object_handlers;
|
|
|
|
return &redis->std;
|
|
}
|
|
|
|
static zend_always_inline RedisSock *
|
|
redis_sock_get_instance(zval *id, int no_throw)
|
|
{
|
|
redis_object *redis;
|
|
|
|
if (Z_TYPE_P(id) == IS_OBJECT) {
|
|
redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, id);
|
|
if (redis->sock) {
|
|
return redis->sock;
|
|
}
|
|
}
|
|
// Throw an exception unless we've been requested not to
|
|
if (!no_throw) {
|
|
REDIS_THROW_EXCEPTION("Redis server went away", 0);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static zend_never_inline ZEND_COLD void redis_sock_throw_exception(RedisSock *redis_sock) {
|
|
char *errmsg = NULL;
|
|
if (redis_sock->status == REDIS_SOCK_STATUS_AUTHENTICATED) {
|
|
if (redis_sock->err != NULL) {
|
|
spprintf(&errmsg, 0, "Could not select database %ld '%s'", redis_sock->dbNumber, ZSTR_VAL(redis_sock->err));
|
|
} else {
|
|
spprintf(&errmsg, 0, "Could not select database %ld", redis_sock->dbNumber);
|
|
}
|
|
} else if (redis_sock->status == REDIS_SOCK_STATUS_CONNECTED) {
|
|
if (redis_sock->err != NULL) {
|
|
spprintf(&errmsg, 0, "Could not authenticate '%s'", ZSTR_VAL(redis_sock->err));
|
|
} else {
|
|
spprintf(&errmsg, 0, "Could not authenticate");
|
|
}
|
|
} else {
|
|
if (redis_sock->port < 0) {
|
|
spprintf(&errmsg, 0, "Redis server %s went away", ZSTR_VAL(redis_sock->host));
|
|
} else {
|
|
spprintf(&errmsg, 0, "Redis server %s:%d went away", ZSTR_VAL(redis_sock->host), redis_sock->port);
|
|
}
|
|
}
|
|
REDIS_THROW_EXCEPTION(errmsg, 0);
|
|
efree(errmsg);
|
|
}
|
|
|
|
/**
|
|
* redis_sock_get
|
|
*/
|
|
PHP_REDIS_API RedisSock *
|
|
redis_sock_get(zval *id, int no_throw)
|
|
{
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_instance(id, no_throw)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (UNEXPECTED(redis_sock_server_open(redis_sock) < 0)) {
|
|
if (!no_throw) {
|
|
redis_sock_throw_exception(redis_sock);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
return redis_sock;
|
|
}
|
|
|
|
/**
|
|
* redis_sock_get_direct
|
|
* Returns our attached RedisSock pointer if we're connected
|
|
*/
|
|
PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
|
|
// If we can't grab our object, or get a socket, or we're not connected,
|
|
// return NULL
|
|
if((zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
|
|
&object, redis_ce) == FAILURE) ||
|
|
(redis_sock = redis_sock_get(object, 1)) == NULL ||
|
|
redis_sock->status < REDIS_SOCK_STATUS_CONNECTED)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Return our socket */
|
|
return redis_sock;
|
|
}
|
|
|
|
static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor)
|
|
{
|
|
if (res->ptr) {
|
|
ConnectionPool *p = res->ptr;
|
|
zend_llist_destroy(&p->list);
|
|
pefree(res->ptr, 1);
|
|
}
|
|
}
|
|
|
|
static void redis_random_hex_bytes(char *dst, size_t dstsize) {
|
|
char chunk[9], *ptr = dst;
|
|
ssize_t rem = dstsize, len, clen;
|
|
size_t bytes;
|
|
|
|
/* We need two characters per hex byte */
|
|
bytes = dstsize / 2;
|
|
zend_string *s = zend_string_alloc(bytes, 0);
|
|
|
|
/* First try to have PHP generate the bytes */
|
|
if (php_random_bytes_silent(ZSTR_VAL(s), bytes) == SUCCESS) {
|
|
php_hash_bin2hex(dst, (unsigned char *)ZSTR_VAL(s), bytes);
|
|
zend_string_release(s);
|
|
return;
|
|
}
|
|
|
|
/* PHP shouldn't have failed, but generate manually if it did. */
|
|
while (rem > 0) {
|
|
clen = snprintf(chunk, sizeof(chunk), "%08x", rand());
|
|
len = rem >= clen ? clen : rem;
|
|
memcpy(ptr, chunk, len);
|
|
ptr += len; rem -= len;
|
|
}
|
|
|
|
zend_string_release(s);
|
|
}
|
|
|
|
/**
|
|
* PHP_MINIT_FUNCTION
|
|
*/
|
|
PHP_MINIT_FUNCTION(redis)
|
|
{
|
|
struct timeval tv;
|
|
|
|
/* Seed random generator (for RedisCluster failover) */
|
|
gettimeofday(&tv, NULL);
|
|
srand(tv.tv_usec * tv.tv_sec);
|
|
|
|
/* Generate our random salt */
|
|
redis_random_hex_bytes(REDIS_G(salt), sizeof(REDIS_G(salt)) - 1);
|
|
REDIS_G(salt)[sizeof(REDIS_G(salt)) - 1] = '\0';
|
|
|
|
REGISTER_INI_ENTRIES();
|
|
|
|
/* Redis class */
|
|
redis_ce = register_class_Redis();
|
|
redis_ce->create_object = create_redis_object;
|
|
|
|
/* RedisArray class */
|
|
ZEND_MINIT(redis_array)(INIT_FUNC_ARGS_PASSTHRU);
|
|
|
|
/* RedisCluster class */
|
|
ZEND_MINIT(redis_cluster)(INIT_FUNC_ARGS_PASSTHRU);
|
|
|
|
/* RedisSentinel class */
|
|
ZEND_MINIT(redis_sentinel)(INIT_FUNC_ARGS_PASSTHRU);
|
|
|
|
/* Register our cluster cache list item */
|
|
le_cluster_slot_cache = zend_register_list_destructors_ex(NULL, cluster_cache_dtor,
|
|
"Redis cluster slot cache",
|
|
module_number);
|
|
|
|
/* RedisException class */
|
|
redis_exception_ce = register_class_RedisException(spl_ce_RuntimeException);
|
|
|
|
#ifdef PHP_SESSION
|
|
php_session_register_module(&ps_mod_redis);
|
|
php_session_register_module(&ps_mod_redis_cluster);
|
|
#endif
|
|
|
|
/* Register resource destructors */
|
|
le_redis_pconnect = zend_register_list_destructors_ex(NULL, redis_connections_pool_dtor,
|
|
"phpredis persistent connections pool", module_number);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
static const char *
|
|
get_available_serializers(void)
|
|
{
|
|
#ifdef HAVE_REDIS_JSON
|
|
#ifdef HAVE_REDIS_IGBINARY
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
return "php, json, igbinary, msgpack";
|
|
#else
|
|
return "php, json, igbinary";
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
return "php, json, msgpack";
|
|
#else
|
|
return "php, json";
|
|
#endif
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_REDIS_IGBINARY
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
return "php, igbinary, msgpack";
|
|
#else
|
|
return "php, igbinary";
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
return "php, msgpack";
|
|
#else
|
|
return "php";
|
|
#endif
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* PHP_MINFO_FUNCTION
|
|
*/
|
|
PHP_MINFO_FUNCTION(redis)
|
|
{
|
|
smart_str names = {0,};
|
|
|
|
php_info_print_table_start();
|
|
php_info_print_table_header(2, "Redis Support", "enabled");
|
|
php_info_print_table_row(2, "Redis Version", PHP_REDIS_VERSION);
|
|
php_info_print_table_row(2, "Redis Sentinel Version", PHP_REDIS_SENTINEL_VERSION);
|
|
#ifdef GIT_REVISION
|
|
php_info_print_table_row(2, "Git revision", "$Id: " GIT_REVISION " $");
|
|
#endif
|
|
php_info_print_table_row(2, "Available serializers", get_available_serializers());
|
|
#ifdef HAVE_REDIS_LZF
|
|
smart_str_appends(&names, "lzf");
|
|
#endif
|
|
#ifdef HAVE_REDIS_ZSTD
|
|
if (names.s) {
|
|
smart_str_appends(&names, ", ");
|
|
}
|
|
smart_str_appends(&names, "zstd");
|
|
#endif
|
|
#ifdef HAVE_REDIS_LZ4
|
|
if (names.s) {
|
|
smart_str_appends(&names, ", ");
|
|
}
|
|
smart_str_appends(&names, "lz4");
|
|
#endif
|
|
if (names.s) {
|
|
smart_str_0(&names);
|
|
php_info_print_table_row(2, "Available compression", ZSTR_VAL(names.s));
|
|
}
|
|
smart_str_free(&names);
|
|
php_info_print_table_end();
|
|
|
|
DISPLAY_INI_ENTRIES();
|
|
}
|
|
|
|
/* {{{ proto Redis Redis::__construct(array $options = null)
|
|
Public constructor */
|
|
PHP_METHOD(Redis, __construct)
|
|
{
|
|
HashTable *opts = NULL;
|
|
redis_object *redis;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(0, 1)
|
|
Z_PARAM_OPTIONAL
|
|
Z_PARAM_ARRAY_HT_OR_NULL(opts)
|
|
ZEND_PARSE_PARAMETERS_END_EX(RETURN_THROWS());
|
|
|
|
redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, getThis());
|
|
redis->sock = redis_sock_create(REDIS_SOCK_STANDALONE, ZEND_STRL("127.0.0.1"),
|
|
6379, 0, 0, 0, NULL, 0);
|
|
if (opts != NULL && redis_sock_configure(redis->sock, opts) != SUCCESS) {
|
|
RETURN_THROWS();
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis Redis::__destruct()
|
|
Public Destructor
|
|
*/
|
|
PHP_METHOD(Redis, __destruct) {
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
// Grab our socket
|
|
RedisSock *redis_sock;
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 1)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
// If we think we're in MULTI mode, send a discard
|
|
if (IS_MULTI(redis_sock)) {
|
|
if (!IS_PIPELINE(redis_sock) && redis_sock->stream) {
|
|
redis_send_discard(redis_sock);
|
|
}
|
|
redis_free_reply_callbacks(redis_sock);
|
|
}
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::connect(string host, int port [, double timeout [, long retry_interval]])
|
|
*/
|
|
PHP_METHOD(Redis, connect)
|
|
{
|
|
if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0) == FAILURE) {
|
|
RETURN_FALSE;
|
|
} else {
|
|
RETURN_TRUE;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::pconnect(string host, int port [, double timeout])
|
|
*/
|
|
PHP_METHOD(Redis, pconnect)
|
|
{
|
|
if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1) == FAILURE) {
|
|
RETURN_FALSE;
|
|
} else {
|
|
RETURN_TRUE;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_REDIS_API int
|
|
redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
|
|
{
|
|
zval *object, *context = NULL, *ele;
|
|
char *host = NULL, *persistent_id = NULL;
|
|
zend_long port = -1, retry_interval = 0;
|
|
size_t host_len, persistent_id_len;
|
|
double timeout = 0.0, read_timeout = 0.0;
|
|
redis_object *redis;
|
|
int af_unix;
|
|
|
|
#ifdef ZTS
|
|
/* not sure how in threaded mode this works so disabled persistence at
|
|
* first */
|
|
persistent = 0;
|
|
#endif
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"Os|lds!lda!", &object, redis_ce, &host,
|
|
&host_len, &port, &timeout, &persistent_id,
|
|
&persistent_id_len, &retry_interval,
|
|
&read_timeout, &context) == FAILURE)
|
|
{
|
|
return FAILURE;
|
|
}
|
|
|
|
/* Disregard persistent_id if we're not opening a persistent connection */
|
|
if (!persistent) {
|
|
persistent_id = NULL;
|
|
}
|
|
|
|
if (timeout > INT_MAX) {
|
|
REDIS_VALUE_EXCEPTION("Invalid connect timeout");
|
|
return FAILURE;
|
|
}
|
|
|
|
if (read_timeout > INT_MAX) {
|
|
REDIS_VALUE_EXCEPTION("Invalid read timeout");
|
|
return FAILURE;
|
|
}
|
|
|
|
if (retry_interval < 0L || retry_interval > INT_MAX) {
|
|
REDIS_VALUE_EXCEPTION("Invalid retry interval");
|
|
return FAILURE;
|
|
}
|
|
|
|
/* Does the host look like a unix socket */
|
|
af_unix = (host_len > 0 && host[0] == '/') ||
|
|
(host_len > 6 && (!strncasecmp(host, "unix://", sizeof("unix://") - 1) ||
|
|
!strncasecmp(host, "file://", sizeof("file://") - 1)));
|
|
|
|
/* If it's not a unix socket, set to default */
|
|
if (port == -1 && !af_unix) {
|
|
port = 6379;
|
|
}
|
|
|
|
redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, object);
|
|
|
|
/* if there is a redis sock already we have to remove it */
|
|
if (redis->sock) {
|
|
redis_sock_disconnect(redis->sock, 0, 1);
|
|
redis_free_socket(redis->sock);
|
|
}
|
|
|
|
redis->sock = redis_sock_create(REDIS_SOCK_STANDALONE, host, host_len, port,
|
|
timeout, read_timeout, persistent,
|
|
persistent_id, retry_interval);
|
|
|
|
if (context) {
|
|
/* Stream context (e.g. TLS) */
|
|
if ((ele = REDIS_HASH_STR_FIND_STATIC(Z_ARRVAL_P(context), "stream"))) {
|
|
redis_sock_set_context_zval(redis->sock, ele);
|
|
}
|
|
|
|
/* AUTH */
|
|
if ((ele = REDIS_HASH_STR_FIND_STATIC(Z_ARRVAL_P(context), "auth"))) {
|
|
redis_sock_set_auth_zval(redis->sock, ele);
|
|
}
|
|
}
|
|
|
|
if (redis_sock_connect(redis->sock) != SUCCESS) {
|
|
if (redis->sock->err) {
|
|
REDIS_THROW_EXCEPTION(ZSTR_VAL(redis->sock->err), 0);
|
|
}
|
|
redis_free_socket(redis->sock);
|
|
redis->sock = NULL;
|
|
return FAILURE;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
static zend_always_inline void
|
|
pipeline_enqueue_cmd_strl(RedisSock *redis_sock, const char *cmd, int len) {
|
|
smart_string_appendl(&redis_sock->pipeline_cmd, cmd, len);
|
|
}
|
|
|
|
static zend_always_inline void
|
|
pipeline_enqueue_cmd(RedisSock *redis_sock, RedisCmd *cmd) {
|
|
pipeline_enqueue_cmd_strl(redis_sock, redis_cmd_str(cmd),
|
|
redis_cmd_len(cmd));
|
|
}
|
|
|
|
static void
|
|
redis_save_callback(RedisSock *redis_sock, FailableResultCallback cb,
|
|
RedisCmdCtx ctx)
|
|
{
|
|
fold_item *fi;
|
|
|
|
fi = redis_add_reply_callback(redis_sock);
|
|
fi->fun = cb;
|
|
fi->flags = redis_sock->flags;
|
|
fi->ctx = ctx;
|
|
}
|
|
|
|
static int
|
|
redis_process_request_strl(RedisSock *redis_sock, const char *cmd, int len) {
|
|
int res = SUCCESS;
|
|
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
pipeline_enqueue_cmd_strl(redis_sock, cmd, len);
|
|
} else if (UNEXPECTED(redis_sock_write(redis_sock, cmd, len) < 0)) {
|
|
res = FAILURE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int
|
|
redis_process_request(RedisSock *redis_sock, RedisCmd *cmd) {
|
|
return redis_process_request_strl(redis_sock, redis_cmd_str(cmd),
|
|
redis_cmd_len(cmd));
|
|
}
|
|
|
|
static void
|
|
redis_process_cmd(INTERNAL_FUNCTION_PARAMETERS, redis_cmd_cb *cmd_cb,
|
|
FailableResultCallback resp_cb)
|
|
{
|
|
RedisSock *redis_sock;
|
|
RedisCmdCtx ctx;
|
|
RedisCmd *cmd;
|
|
int res;
|
|
|
|
redis_sock = redis_sock_get(getThis(), 0);
|
|
if (UNEXPECTED(redis_sock == NULL)) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
cmd = cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
|
|
if (UNEXPECTED(cmd == NULL)) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
ctx = redis_cmd_pop_ctx(cmd);
|
|
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
pipeline_enqueue_cmd(redis_sock, cmd);
|
|
res = SUCCESS;
|
|
} else {
|
|
res = redis_sock_write_cmd(redis_sock, cmd) < 0 ? FAILURE : SUCCESS;
|
|
}
|
|
|
|
redis_cmd_free(cmd);
|
|
|
|
if (UNEXPECTED(res != SUCCESS)) {
|
|
redis_cmd_ctx_free(ctx);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
resp_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx);
|
|
redis_cmd_ctx_free(ctx);
|
|
} else {
|
|
if (!IS_PIPELINE(redis_sock)) {
|
|
if (redis_response_enqueued(redis_sock) != SUCCESS) {
|
|
redis_cmd_ctx_free(ctx);
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
redis_save_callback(redis_sock, resp_cb, ctx);
|
|
RETURN_ZVAL(getThis(), 1, 0);
|
|
}
|
|
}
|
|
|
|
#define REDIS_PROCESS_CMD(cmdname, resp_func) \
|
|
redis_process_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, \
|
|
redis_##cmdname##_cmd, resp_func)
|
|
|
|
void
|
|
redis_process_kw_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw,
|
|
redis_kw_cmd_cb *cmd_cb, FailableResultCallback resp_cb)
|
|
{
|
|
RedisSock *redis_sock;
|
|
RedisCmdCtx ctx;
|
|
RedisCmd *cmd;
|
|
int res;
|
|
|
|
redis_sock = redis_sock_get(getThis(), 0);
|
|
if (UNEXPECTED(redis_sock == NULL)) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
cmd = cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, (char*)kw);
|
|
if (UNEXPECTED(cmd == NULL)) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
ctx = redis_cmd_pop_ctx(cmd);
|
|
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
pipeline_enqueue_cmd(redis_sock, cmd);
|
|
res = SUCCESS;
|
|
} else {
|
|
res = redis_sock_write_cmd(redis_sock, cmd) < 0 ? FAILURE : SUCCESS;
|
|
}
|
|
|
|
redis_cmd_free(cmd);
|
|
|
|
if (UNEXPECTED(res != SUCCESS)) {
|
|
redis_cmd_ctx_free(ctx);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
resp_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx);
|
|
redis_cmd_ctx_free(ctx);
|
|
} else {
|
|
if (!IS_PIPELINE(redis_sock)) {
|
|
if (redis_response_enqueued(redis_sock) != SUCCESS) {
|
|
redis_cmd_ctx_free(ctx);
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
redis_save_callback(redis_sock, resp_cb, ctx);
|
|
RETURN_ZVAL(getThis(), 1, 0);
|
|
}
|
|
}
|
|
|
|
/* {{{ proto long Redis::bitop(string op, string key, ...) */
|
|
PHP_METHOD(Redis, bitop) {
|
|
REDIS_PROCESS_CMD(bitop, redis_long_response);
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::bitcount(string key, [int start], [int end])
|
|
*/
|
|
PHP_METHOD(Redis, bitcount)
|
|
{
|
|
REDIS_PROCESS_CMD(bitcount, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto integer Redis::bitpos(string key, int bit, [int start, int end]) */
|
|
PHP_METHOD(Redis, bitpos)
|
|
{
|
|
REDIS_PROCESS_CMD(bitpos, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::close()
|
|
*/
|
|
PHP_METHOD(Redis, close)
|
|
{
|
|
RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU);
|
|
|
|
if (redis_sock_disconnect(redis_sock, 1, 1) == SUCCESS) {
|
|
RETURN_TRUE;
|
|
}
|
|
RETURN_FALSE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::set(string key, mixed val, long timeout,
|
|
* [array opt) */
|
|
PHP_METHOD(Redis, set) {
|
|
REDIS_PROCESS_CMD(set, redis_set_response);
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::setex(string key, long expire, string value)
|
|
*/
|
|
PHP_METHOD(Redis, setex)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, redis_boolean_response);
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::psetex(string key, long expire, string value)
|
|
*/
|
|
PHP_METHOD(Redis, psetex)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, redis_boolean_response);
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::setnx(string key, string value)
|
|
*/
|
|
PHP_METHOD(Redis, setnx)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SETNX", redis_kv_cmd, redis_1_response);
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::getSet(string key, string value)
|
|
*/
|
|
PHP_METHOD(Redis, getset)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("GETSET", redis_kv_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::randomKey()
|
|
*/
|
|
PHP_METHOD(Redis, randomKey)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RANDOMKEY", redis_empty_cmd, redis_ping_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::echo(string msg)
|
|
*/
|
|
PHP_METHOD(Redis, echo)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ECHO", redis_str_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::rename(string key_src, string key_dst)
|
|
*/
|
|
PHP_METHOD(Redis, rename)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::renameNx(string key_src, string key_dst)
|
|
*/
|
|
PHP_METHOD(Redis, renameNx)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RENAMENX", redis_key_key_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/** {{{ proto bool Redis::reset()
|
|
*/
|
|
PHP_METHOD(Redis, reset)
|
|
{
|
|
char *response;
|
|
int response_len;
|
|
RedisSock *redis_sock;
|
|
RedisCmd *cmd;
|
|
zend_bool ret = 0;
|
|
|
|
if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
php_error_docref(NULL, E_ERROR,
|
|
"Reset isn't allowed in pipeline mode!");
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
cmd = redis_cmd_create_literal(NULL, "RESET");
|
|
|
|
if (redis_process_request(redis_sock, cmd) != SUCCESS) {
|
|
redis_cmd_free(cmd);
|
|
RETURN_FALSE;
|
|
}
|
|
redis_cmd_free(cmd);
|
|
|
|
if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) {
|
|
ret = REDIS_STRCMP_STATIC(response, response_len, "+RESET");
|
|
efree(response);
|
|
}
|
|
|
|
if (!ret) {
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
RETURN_FALSE;
|
|
}
|
|
REDIS_THROW_EXCEPTION("Reset failed in multi mode!", 0);
|
|
RETURN_ZVAL(getThis(), 1, 0);
|
|
}
|
|
|
|
redis_free_reply_callbacks(redis_sock);
|
|
redis_sock->status = REDIS_SOCK_STATUS_CONNECTED;
|
|
redis_sock->mode = ATOMIC;
|
|
redis_sock->dbNumber = 0;
|
|
redis_sock->watching = 0;
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::get(string key)
|
|
*/
|
|
PHP_METHOD(Redis, get)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis|array|false Redis::getWithMeta(string key)
|
|
*/
|
|
PHP_METHOD(Redis, getWithMeta)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_bulk_withmeta_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::getDel(string key)
|
|
*/
|
|
PHP_METHOD(Redis, getDel)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("GETDEL", redis_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::getEx(string key [, array $options = []])
|
|
*/
|
|
PHP_METHOD(Redis, getEx)
|
|
{
|
|
REDIS_PROCESS_CMD(getex, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::ping()
|
|
*/
|
|
PHP_METHOD(Redis, ping)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("PING", redis_opt_str_cmd, redis_read_variant_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::incr(string key [,int value])
|
|
*/
|
|
PHP_METHOD(Redis, incr){
|
|
REDIS_PROCESS_CMD(incr, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::incrBy(string key ,int value)
|
|
*/
|
|
PHP_METHOD(Redis, incrBy){
|
|
REDIS_PROCESS_KW_CMD("INCRBY", redis_key_long_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto float Redis::incrByFloat(string key, float value)
|
|
*/
|
|
PHP_METHOD(Redis, incrByFloat) {
|
|
REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, redis_bulk_double_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::decr(string key) */
|
|
PHP_METHOD(Redis, decr)
|
|
{
|
|
REDIS_PROCESS_CMD(decr, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::decrBy(string key ,int value)
|
|
*/
|
|
PHP_METHOD(Redis, decrBy){
|
|
REDIS_PROCESS_KW_CMD("DECRBY", redis_key_long_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::mget(array keys)
|
|
*/
|
|
PHP_METHOD(Redis, mget) {
|
|
REDIS_PROCESS_CMD(mget, redis_sock_read_multibulk_reply);
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::exists(string $key, string ...$more_keys)
|
|
*/
|
|
PHP_METHOD(Redis, exists) {
|
|
REDIS_PROCESS_KW_CMD("EXISTS", redis_varkey_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::touch(string $key, string ...$more_keys)
|
|
*/
|
|
PHP_METHOD(Redis, touch) {
|
|
REDIS_PROCESS_KW_CMD("TOUCH", redis_varkey_cmd, redis_long_response);
|
|
}
|
|
|
|
/* }}} */
|
|
/* {{{ proto boolean Redis::del(string key)
|
|
*/
|
|
PHP_METHOD(Redis, del) {
|
|
REDIS_PROCESS_KW_CMD("DEL", redis_varkey_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, delex) {
|
|
REDIS_PROCESS_CMD(delex, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, delifeq) {
|
|
REDIS_PROCESS_KW_CMD("DELIFEQ", redis_kv_cmd, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto long Redis::unlink(string $key1, string $key2 [, string $key3...]) }}}
|
|
* {{{ proto long Redis::unlink(array $keys) */
|
|
PHP_METHOD(Redis, unlink)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("UNLINK", redis_varkey_cmd, redis_long_response);
|
|
}
|
|
|
|
PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock)
|
|
{
|
|
redis_sock->watching = 1;
|
|
}
|
|
|
|
PHP_REDIS_API int
|
|
redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
|
|
zval *z_tab, RedisCmdCtx ctx)
|
|
{
|
|
return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock, z_tab, ctx, redis_set_watch);
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::watch(string key1, string key2...)
|
|
*/
|
|
PHP_METHOD(Redis, watch) {
|
|
REDIS_PROCESS_KW_CMD("WATCH", redis_varkey_cmd, redis_watch_response);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock)
|
|
{
|
|
redis_sock->watching = 0;
|
|
}
|
|
|
|
PHP_REDIS_API int redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS,
|
|
RedisSock *redis_sock, zval *z_tab,
|
|
RedisCmdCtx ctx)
|
|
{
|
|
return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
|
|
z_tab, ctx, redis_clear_watch);
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::unwatch()
|
|
*/
|
|
PHP_METHOD(Redis, unwatch)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("UNWATCH", redis_empty_cmd, redis_unwatch_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::keys(string pattern)
|
|
*/
|
|
PHP_METHOD(Redis, keys)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("KEYS", redis_key_cmd, redis_mbulk_reply_raw);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int Redis::type(string key)
|
|
*/
|
|
PHP_METHOD(Redis, type)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("TYPE", redis_key_cmd, redis_type_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto mixed Redis::acl(string $op, ...) }}} */
|
|
PHP_METHOD(Redis, acl) {
|
|
REDIS_PROCESS_CMD(acl, redis_acl_response);
|
|
}
|
|
|
|
/* {{{ proto long Redis::append(string key, string val) */
|
|
PHP_METHOD(Redis, append)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("APPEND", redis_kv_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::GetRange(string key, long start, long end) */
|
|
PHP_METHOD(Redis, getRange)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd,
|
|
redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto mixed Redis::lcs(string $key1, string $key2, ?array $options = NULL); */
|
|
PHP_METHOD(Redis, lcs) {
|
|
REDIS_PROCESS_CMD(lcs, redis_read_variant_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::setRange(string key, long start, string value) */
|
|
PHP_METHOD(Redis, setRange)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd,
|
|
redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::getbit(string key, long idx) */
|
|
PHP_METHOD(Redis, getBit)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("GETBIT", redis_key_long_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::setbit(string key, long idx, bool|int value) */
|
|
PHP_METHOD(Redis, setBit)
|
|
{
|
|
REDIS_PROCESS_CMD(setbit, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::strlen(string key) */
|
|
PHP_METHOD(Redis, strlen)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("STRLEN", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::lPush(string key , string value)
|
|
*/
|
|
PHP_METHOD(Redis, lPush)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LPUSH", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::rPush(string key , string value)
|
|
*/
|
|
PHP_METHOD(Redis, rPush)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RPUSH", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, lInsert)
|
|
{
|
|
REDIS_PROCESS_CMD(linsert, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto long Redis::lPushx(string key, mixed value) */
|
|
PHP_METHOD(Redis, lPushx)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::rPushx(string key, mixed value) */
|
|
PHP_METHOD(Redis, rPushx)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::lPop(string key, [int count = 0]) */
|
|
PHP_METHOD(Redis, lPop)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LPOP", redis_pop_cmd, redis_pop_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::lPos(string key, mixed value, [array options = null]) */
|
|
PHP_METHOD(Redis, lPos)
|
|
{
|
|
REDIS_PROCESS_CMD(lpos, redis_lpos_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::rPop(string key, [int count = 0]) */
|
|
PHP_METHOD(Redis, rPop)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RPOP", redis_pop_cmd, redis_pop_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::blPop(string key1, string key2, ..., int timeout) */
|
|
PHP_METHOD(Redis, blPop)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("BLPOP", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::brPop(string key1, string key2, ..., int timeout) */
|
|
PHP_METHOD(Redis, brPop)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("BRPOP", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto int Redis::lLen(string key) */
|
|
PHP_METHOD(Redis, lLen)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LLEN", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::blMove(string source, string destination, string wherefrom, string whereto, double $timeout) */
|
|
PHP_METHOD(Redis, blmove) {
|
|
REDIS_PROCESS_KW_CMD("BLMOVE", redis_lmove_cmd, redis_string_response);
|
|
}
|
|
|
|
/* {{{ proto string Redis::lMove(string source, string destination, string wherefrom, string whereto) */
|
|
PHP_METHOD(Redis, lMove) {
|
|
REDIS_PROCESS_KW_CMD("LMOVE", redis_lmove_cmd, redis_string_response);
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::lrem(string list, string value, int count = 0) */
|
|
PHP_METHOD(Redis, lrem)
|
|
{
|
|
REDIS_PROCESS_CMD(lrem, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::ltrim(string key , int start , int end) */
|
|
PHP_METHOD(Redis, ltrim)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd,
|
|
redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::lindex(string key , int index) */
|
|
PHP_METHOD(Redis, lindex)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::lrange(string key, int start , int end) */
|
|
PHP_METHOD(Redis, lrange)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd,
|
|
redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::sAdd(string key , mixed value) */
|
|
PHP_METHOD(Redis, sAdd)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::sAddArray(string key, array $values) */
|
|
PHP_METHOD(Redis, sAddArray) {
|
|
REDIS_PROCESS_KW_CMD("SADD", redis_key_val_arr_cmd, redis_long_response);
|
|
} /* }}} */
|
|
|
|
/* {{{ proto int Redis::scard(string key) */
|
|
PHP_METHOD(Redis, scard)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SCARD", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::srem(string set, string value) */
|
|
PHP_METHOD(Redis, srem)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::sMove(string src, string dst, mixed value) */
|
|
PHP_METHOD(Redis, sMove)
|
|
{
|
|
REDIS_PROCESS_CMD(smove, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::sPop(string key) */
|
|
PHP_METHOD(Redis, sPop)
|
|
{
|
|
if (ZEND_NUM_ARGS() == 1) {
|
|
REDIS_PROCESS_KW_CMD("SPOP", redis_key_cmd, redis_string_response);
|
|
} else if (ZEND_NUM_ARGS() == 2) {
|
|
REDIS_PROCESS_KW_CMD("SPOP", redis_key_long_cmd, redis_sock_read_multibulk_reply);
|
|
} else {
|
|
zend_wrong_param_count();
|
|
}
|
|
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::sRandMember(string key [int count]) */
|
|
PHP_METHOD(Redis, sRandMember)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SRANDMEMBER", redis_randmember_cmd,
|
|
redis_randmember_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::sismember(string set, string value) */
|
|
PHP_METHOD(Redis, sismember)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sMembers(string set) */
|
|
PHP_METHOD(Redis, sMembers)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd,
|
|
redis_sock_read_multibulk_reply);
|
|
}
|
|
|
|
/* {{{ proto array Redis::sMisMember(string key, string member0, ...memberN) */
|
|
PHP_METHOD(Redis, sMisMember)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SMISMEMBER", redis_key_varval_cmd, redis_read_variant_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sInter(string key0, ... string keyN) */
|
|
PHP_METHOD(Redis, sInter) {
|
|
REDIS_PROCESS_KW_CMD("SINTER", redis_varkey_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, sintercard) {
|
|
REDIS_PROCESS_KW_CMD("SINTERCARD", redis_intercard_cmd, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto array Redis::sInterStore(string dst, string key0,...string keyN) */
|
|
PHP_METHOD(Redis, sInterStore) {
|
|
REDIS_PROCESS_KW_CMD("SINTERSTORE", redis_varkey_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sUnion(string key0, ... string keyN) */
|
|
PHP_METHOD(Redis, sUnion) {
|
|
REDIS_PROCESS_KW_CMD("SUNION", redis_varkey_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sUnionStore(array|string $key, string ...$srckeys) */
|
|
PHP_METHOD(Redis, sUnionStore) {
|
|
REDIS_PROCESS_KW_CMD("SUNIONSTORE", redis_varkey_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sDiff(string key0, ... string keyN) */
|
|
PHP_METHOD(Redis, sDiff) {
|
|
REDIS_PROCESS_KW_CMD("SDIFF", redis_varkey_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sDiffStore(string dst, string key0, ... keyN) */
|
|
PHP_METHOD(Redis, sDiffStore) {
|
|
REDIS_PROCESS_KW_CMD("SDIFFSTORE", redis_varkey_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sort(string key, array options) */
|
|
PHP_METHOD(Redis, sort) {
|
|
REDIS_PROCESS_KW_CMD("SORT", redis_sort_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto array Redis::sort(string key, array options) */
|
|
PHP_METHOD(Redis, sort_ro) {
|
|
REDIS_PROCESS_KW_CMD("SORT_RO", redis_sort_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
static RedisCmd *
|
|
generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int desc,
|
|
int alpha)
|
|
{
|
|
zend_string *key = NULL, *pattern = NULL, *store = NULL, *zpattern;
|
|
zval *object, *zele, *zget = NULL;
|
|
zend_long offset = -1, count = -1;
|
|
RedisCmd *cmd;
|
|
|
|
/* Parse myriad of sort arguments */
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"OS|S!z!llS", &object, redis_ce, &key,
|
|
&pattern, &zget, &offset, &count, &store)
|
|
== FAILURE)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Ensure we're sorting something, and we can get context */
|
|
if (ZSTR_LEN(key) == 0)
|
|
return NULL;
|
|
|
|
/* Start constructing final command and append key */
|
|
cmd = redis_cmd_create_literal(redis_sock, "SORT");
|
|
if (!redis_cmd_cat_key_zstr(cmd, key)) {
|
|
redis_cmd_free(cmd);
|
|
return NULL;
|
|
}
|
|
|
|
/* BY pattern */
|
|
if (pattern && ZSTR_LEN(pattern)) {
|
|
redis_cmd_cat_literal(cmd, "BY");
|
|
redis_cmd_cat_zstr(cmd, pattern);
|
|
}
|
|
|
|
/* LIMIT offset count */
|
|
if (offset >= 0 && count >= 0) {
|
|
redis_cmd_cat_literal(cmd, "LIMIT");
|
|
redis_cmd_cat_long(cmd, offset);
|
|
redis_cmd_cat_long(cmd, count);
|
|
}
|
|
|
|
/* Handle any number of GET pattern arguments we've been passed */
|
|
if (zget != NULL) {
|
|
if (Z_TYPE_P(zget) == IS_ARRAY) {
|
|
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zget), zele) {
|
|
zpattern = zval_get_string(zele);
|
|
redis_cmd_cat_literal(cmd, "GET");
|
|
redis_cmd_cat_zstr(cmd, zpattern);
|
|
zend_string_release(zpattern);
|
|
} ZEND_HASH_FOREACH_END();
|
|
} else {
|
|
zpattern = zval_get_string(zget);
|
|
redis_cmd_cat_literal(cmd, "GET");
|
|
redis_cmd_cat_zstr(cmd, zpattern);
|
|
zend_string_release(zpattern);
|
|
}
|
|
}
|
|
|
|
/* Append optional DESC and ALPHA modifiers */
|
|
if (desc) redis_cmd_cat_literal(cmd, "DESC");
|
|
if (alpha) redis_cmd_cat_literal(cmd, "ALPHA");
|
|
|
|
/* Finally append STORE if we've got it */
|
|
if (store && ZSTR_LEN(store)) {
|
|
redis_cmd_cat_literal(cmd, "STORE");
|
|
if (!redis_cmd_cat_key_zstr(cmd, store)) {
|
|
redis_cmd_free(cmd);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return cmd;
|
|
}
|
|
|
|
#define REDIS_SORT_CMD_FUNC(name, desc, alpha) \
|
|
RedisCmd * \
|
|
redis_##name##_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { \
|
|
return generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, \
|
|
desc, alpha); \
|
|
}
|
|
|
|
REDIS_SORT_CMD_FUNC(sort_asc_numeric, 0, 0)
|
|
REDIS_SORT_CMD_FUNC(sort_asc_alpha, 0, 1)
|
|
REDIS_SORT_CMD_FUNC(sort_desc_numeric, 1, 0)
|
|
REDIS_SORT_CMD_FUNC(sort_desc_alpha, 1, 1)
|
|
|
|
PHP_METHOD(Redis, sortAsc) {
|
|
REDIS_PROCESS_CMD(sort_asc_numeric, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, sortAscAlpha){
|
|
REDIS_PROCESS_CMD(sort_asc_alpha, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, sortDesc) {
|
|
REDIS_PROCESS_CMD(sort_desc_numeric, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, sortDescAlpha) {
|
|
REDIS_PROCESS_CMD(sort_desc_alpha, redis_read_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto array Redis::expire(string key, int timeout) */
|
|
PHP_METHOD(Redis, expire) {
|
|
REDIS_PROCESS_KW_CMD("EXPIRE", redis_expire_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::pexpire(string key, long ms) */
|
|
PHP_METHOD(Redis, pexpire) {
|
|
REDIS_PROCESS_KW_CMD("PEXPIRE", redis_expire_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::expireAt(string key, int timestamp) */
|
|
PHP_METHOD(Redis, expireAt) {
|
|
REDIS_PROCESS_KW_CMD("EXPIREAT", redis_expire_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::pexpireAt(string key, int timestamp) */
|
|
PHP_METHOD(Redis, pexpireAt) {
|
|
REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_expire_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::expiretime(string $key): int */
|
|
PHP_METHOD(Redis, expiretime) {
|
|
REDIS_PROCESS_KW_CMD("EXPIRETIME", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::expiretime(string $key): int */
|
|
PHP_METHOD(Redis, pexpiretime) {
|
|
REDIS_PROCESS_KW_CMD("PEXPIRETIME", redis_key_cmd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, expiremember) {
|
|
REDIS_PROCESS_CMD(expiremember, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, expirememberat) {
|
|
REDIS_PROCESS_CMD(expirememberat, redis_long_response);
|
|
}
|
|
|
|
/* }}} */
|
|
/* {{{ proto array Redis::lSet(string key, int index, string value) */
|
|
PHP_METHOD(Redis, lSet) {
|
|
REDIS_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd,
|
|
redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::save() */
|
|
PHP_METHOD(Redis, save)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SAVE", redis_empty_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::bgSave() */
|
|
PHP_METHOD(Redis, bgSave)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("BGSAVE", redis_empty_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto integer Redis::lastSave() */
|
|
PHP_METHOD(Redis, lastSave)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LASTSAVE", redis_empty_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::failover([array to [,bool abort [,int timeout]]] ) */
|
|
PHP_METHOD(Redis, failover)
|
|
{
|
|
REDIS_PROCESS_CMD(failover, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::flushDB([bool async]) */
|
|
PHP_METHOD(Redis, flushDB)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("FLUSHDB", redis_flush_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::flushAll([bool async]) */
|
|
PHP_METHOD(Redis, flushAll)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("FLUSHALL", redis_flush_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto mixed Redis::function(string op, mixed ...args) */
|
|
PHP_METHOD(Redis, function)
|
|
{
|
|
REDIS_PROCESS_CMD(function, redis_function_response);
|
|
}
|
|
|
|
/* {{{ proto int Redis::dbSize() */
|
|
PHP_METHOD(Redis, dbSize)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("DBSIZE", redis_empty_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::auth(string passwd) */
|
|
PHP_METHOD(Redis, auth) {
|
|
REDIS_PROCESS_CMD(auth, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::persist(string key) */
|
|
PHP_METHOD(Redis, persist) {
|
|
REDIS_PROCESS_KW_CMD("PERSIST", redis_key_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto long Redis::ttl(string key) */
|
|
PHP_METHOD(Redis, ttl) {
|
|
REDIS_PROCESS_KW_CMD("TTL", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::pttl(string key) */
|
|
PHP_METHOD(Redis, pttl) {
|
|
REDIS_PROCESS_KW_CMD("PTTL", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::info() */
|
|
PHP_METHOD(Redis, info) {
|
|
REDIS_PROCESS_CMD(info, redis_info_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::select(long dbNumber) */
|
|
PHP_METHOD(Redis, select) {
|
|
REDIS_PROCESS_CMD(select, redis_select_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::swapdb(long srcdb, long dstdb) */
|
|
PHP_METHOD(Redis, swapdb) {
|
|
REDIS_PROCESS_KW_CMD("SWAPDB", redis_long_long_cmd, redis_boolean_response);
|
|
}
|
|
|
|
/* {{{ proto bool Redis::move(string key, long dbindex) */
|
|
PHP_METHOD(Redis, move) {
|
|
REDIS_PROCESS_KW_CMD("MOVE", redis_key_long_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::mset(array (key => value, ...)) */
|
|
PHP_METHOD(Redis, mset) {
|
|
REDIS_PROCESS_KW_CMD("MSET", redis_mset_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto bool Redis::msetnx(array (key => value, ...)) */
|
|
PHP_METHOD(Redis, msetnx) {
|
|
REDIS_PROCESS_KW_CMD("MSETNX", redis_mset_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, msetex) {
|
|
REDIS_PROCESS_CMD(msetex, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto string Redis::rpoplpush(string srckey, string dstkey) */
|
|
PHP_METHOD(Redis, rpoplpush)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::brpoplpush(string src, string dst, int timeout) */
|
|
PHP_METHOD(Redis, brpoplpush) {
|
|
REDIS_PROCESS_CMD(brpoplpush, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zAdd(string key, int score, string value) */
|
|
PHP_METHOD(Redis, zAdd) {
|
|
REDIS_PROCESS_CMD(zadd, redis_zadd_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zRandMember(string key, array options) */
|
|
PHP_METHOD(Redis, zRandMember) {
|
|
REDIS_PROCESS_CMD(zrandmember, redis_zrandmember_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zRange(string key,int start,int end,bool scores = 0) */
|
|
PHP_METHOD(Redis, zRange) {
|
|
REDIS_PROCESS_KW_CMD("ZRANGE", redis_zrange_cmd, redis_zrange_response);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, zrangestore) {
|
|
REDIS_PROCESS_KW_CMD("ZRANGESTORE", redis_zrange_cmd, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores = 0) */
|
|
PHP_METHOD(Redis, zRevRange) {
|
|
REDIS_PROCESS_KW_CMD("ZREVRANGE", redis_zrange_cmd, redis_zrange_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zRangeByScore(string k,string s,string e,array opt) */
|
|
PHP_METHOD(Redis, zRangeByScore) {
|
|
REDIS_PROCESS_KW_CMD("ZRANGEBYSCORE", redis_zrange_cmd, redis_zrange_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zRevRangeByScore(string key, string start, string end,
|
|
* array options) */
|
|
PHP_METHOD(Redis, zRevRangeByScore) {
|
|
REDIS_PROCESS_KW_CMD("ZREVRANGEBYSCORE", redis_zrange_cmd, redis_zrange_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zRangeByLex(string key, string min, string max, [
|
|
* offset, limit]) */
|
|
PHP_METHOD(Redis, zRangeByLex) {
|
|
REDIS_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd,
|
|
redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, zRevRangeByLex) {
|
|
REDIS_PROCESS_KW_CMD("ZREVRANGEBYLEX", redis_zrangebylex_cmd,
|
|
redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zLexCount(string key, string min, string max) */
|
|
PHP_METHOD(Redis, zLexCount) {
|
|
REDIS_PROCESS_KW_CMD("ZLEXCOUNT", redis_gen_zlex_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRemRangeByLex(string key, string min, string max) */
|
|
PHP_METHOD(Redis, zRemRangeByLex) {
|
|
REDIS_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd,
|
|
redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRem(string key, string member) */
|
|
PHP_METHOD(Redis, zRem)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRemRangeByScore(string k, string s, string e) */
|
|
PHP_METHOD(Redis, zRemRangeByScore)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd,
|
|
redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRemRangeByRank(string key, long start, long end) */
|
|
PHP_METHOD(Redis, zRemRangeByRank)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd,
|
|
redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zCount(string key, string start , string end) */
|
|
PHP_METHOD(Redis, zCount)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZCOUNT", redis_key_str_str_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zCard(string key) */
|
|
PHP_METHOD(Redis, zCard)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZCARD", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto double Redis::zScore(string key, mixed member) */
|
|
PHP_METHOD(Redis, zScore)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd,
|
|
redis_bulk_double_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zMscore(string key, string member0, ...memberN) */
|
|
PHP_METHOD(Redis, zMscore)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZMSCORE", redis_key_varval_cmd, redis_mbulk_reply_double);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRank(string key, string member) */
|
|
PHP_METHOD(Redis, zRank) {
|
|
REDIS_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRevRank(string key, string member) */
|
|
PHP_METHOD(Redis, zRevRank) {
|
|
REDIS_PROCESS_KW_CMD("ZREVRANK", redis_kv_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto double Redis::zIncrBy(string key, double value, mixed member) */
|
|
PHP_METHOD(Redis, zIncrBy)
|
|
{
|
|
REDIS_PROCESS_CMD(zincrby, redis_bulk_double_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zdiff(array keys, array options) */
|
|
PHP_METHOD(Redis, zdiff) {
|
|
REDIS_PROCESS_CMD(zdiff, redis_zdiff_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zinter(array keys, array|null weights, array options) */
|
|
PHP_METHOD(Redis, zinter) {
|
|
REDIS_PROCESS_KW_CMD("ZINTER", redis_zinterunion_cmd, redis_zdiff_response);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, zintercard) {
|
|
REDIS_PROCESS_KW_CMD("ZINTERCARD", redis_intercard_cmd, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto array Redis::zunion(array keys, array|null weights, array options) */
|
|
PHP_METHOD(Redis, zunion) {
|
|
REDIS_PROCESS_KW_CMD("ZUNION", redis_zinterunion_cmd, redis_zdiff_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zdiffstore(string destination, array keys) */
|
|
PHP_METHOD(Redis, zdiffstore) {
|
|
REDIS_PROCESS_CMD(zdiffstore, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* zinterstore */
|
|
PHP_METHOD(Redis, zinterstore) {
|
|
REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinterunionstore_cmd, redis_long_response);
|
|
}
|
|
|
|
/* zunionstore */
|
|
PHP_METHOD(Redis, zunionstore) {
|
|
REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinterunionstore_cmd, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto array Redis::zPopMax(string key) */
|
|
PHP_METHOD(Redis, zPopMax)
|
|
{
|
|
if (ZEND_NUM_ARGS() == 1) {
|
|
REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, redis_mbulk_reply_zipped_keys_dbl);
|
|
} else if (ZEND_NUM_ARGS() == 2) {
|
|
REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl);
|
|
} else {
|
|
zend_wrong_param_count();
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zPopMin(string key) */
|
|
PHP_METHOD(Redis, zPopMin)
|
|
{
|
|
if (ZEND_NUM_ARGS() == 1) {
|
|
REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, redis_mbulk_reply_zipped_keys_dbl);
|
|
} else if (ZEND_NUM_ARGS() == 2) {
|
|
REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl);
|
|
} else {
|
|
zend_wrong_param_count();
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::bzPopMax(Array[keys] [, timeout]): Array */
|
|
PHP_METHOD(Redis, bzPopMax) {
|
|
REDIS_PROCESS_KW_CMD("BZPOPMAX", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::bzPopMin([keys] [, timeout]): Array */
|
|
PHP_METHOD(Redis, bzPopMin) {
|
|
REDIS_PROCESS_KW_CMD("BZPOPMIN", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis|array|false Redis::lmpop(array $keys, string $from, int $count = 1) */
|
|
PHP_METHOD(Redis, lmpop) {
|
|
REDIS_PROCESS_KW_CMD("LMPOP", redis_mpop_cmd, redis_mpop_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis|array|false Redis::blmpop(double $timeout, array $keys, string $from, int $count = 1) */
|
|
PHP_METHOD(Redis, blmpop) {
|
|
REDIS_PROCESS_KW_CMD("BLMPOP", redis_mpop_cmd, redis_mpop_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis|array|false Redis::zmpop(array $keys, string $from, int $count = 1) */
|
|
PHP_METHOD(Redis, zmpop) {
|
|
REDIS_PROCESS_KW_CMD("ZMPOP", redis_mpop_cmd, redis_mpop_response);
|
|
}
|
|
|
|
/* {{{ proto Redis|array|false Redis::bzmpop(double $timeout, array $keys, string $from, int $count = 1) */
|
|
PHP_METHOD(Redis, bzmpop) {
|
|
REDIS_PROCESS_KW_CMD("BZMPOP", redis_mpop_cmd, redis_mpop_response);
|
|
}
|
|
|
|
/* }}} */
|
|
/* hashes */
|
|
|
|
/* {{{ proto long Redis::hset(string key, string mem, string val) */
|
|
PHP_METHOD(Redis, hSet)
|
|
{
|
|
REDIS_PROCESS_CMD(hset, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::hSetNx(string key, string mem, string val) */
|
|
PHP_METHOD(Redis, hSetNx)
|
|
{
|
|
REDIS_PROCESS_CMD(hsetnx, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::hget(string key, string mem) */
|
|
PHP_METHOD(Redis, hGet)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HGET", redis_key_str_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::hgetWithMeta(string key, string mem) */
|
|
PHP_METHOD(Redis, hGetWithMeta)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HGET", redis_key_str_cmd, redis_bulk_withmeta_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::hLen(string key) */
|
|
PHP_METHOD(Redis, hLen)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HLEN", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::hDel(string key, string mem1, ... memN) */
|
|
PHP_METHOD(Redis, hDel)
|
|
{
|
|
REDIS_PROCESS_CMD(hdel, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::hExists(string key, string mem) */
|
|
PHP_METHOD(Redis, hExists)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HEXISTS", redis_key_str_cmd, redis_1_response);
|
|
}
|
|
|
|
/* {{{ proto array Redis::hkeys(string key) */
|
|
PHP_METHOD(Redis, hKeys)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HKEYS", redis_key_cmd, redis_mbulk_reply_raw);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::hvals(string key) */
|
|
PHP_METHOD(Redis, hVals)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HVALS", redis_key_cmd,
|
|
redis_sock_read_multibulk_reply);
|
|
}
|
|
|
|
/* {{{ proto array Redis::hgetall(string key) */
|
|
PHP_METHOD(Redis, hGetAll) {
|
|
REDIS_PROCESS_KW_CMD("HGETALL", redis_key_cmd, redis_mbulk_reply_zipped_vals);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto double Redis::hIncrByFloat(string k, string me, double v) */
|
|
PHP_METHOD(Redis, hIncrByFloat)
|
|
{
|
|
REDIS_PROCESS_CMD(hincrbyfloat, redis_bulk_double_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::hincrby(string key, string mem, long byval) */
|
|
PHP_METHOD(Redis, hIncrBy)
|
|
{
|
|
REDIS_PROCESS_CMD(hincrby, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ array Redis::hMget(string hash, array keys) */
|
|
PHP_METHOD(Redis, hMget) {
|
|
REDIS_PROCESS_CMD(hmget, redis_mbulk_reply_assoc);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, hgetex) {
|
|
REDIS_PROCESS_CMD(hgetex, redis_mbulk_reply_assoc);
|
|
}
|
|
|
|
PHP_METHOD(Redis, hsetex) {
|
|
REDIS_PROCESS_CMD(hsetex, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, hgetdel) {
|
|
REDIS_PROCESS_CMD(hgetdel, redis_mbulk_reply_assoc);
|
|
}
|
|
|
|
/* {{{ proto bool Redis::hmset(string key, array keyvals) */
|
|
PHP_METHOD(Redis, hMset)
|
|
{
|
|
REDIS_PROCESS_CMD(hmset, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, hexpire) {
|
|
REDIS_PROCESS_KW_CMD("HEXPIRE", redis_hexpire_cmd,
|
|
redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, hpexpire) {
|
|
REDIS_PROCESS_KW_CMD("HPEXPIRE", redis_hexpire_cmd,
|
|
redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, hexpireat) {
|
|
REDIS_PROCESS_KW_CMD("HEXPIREAT", redis_hexpire_cmd,
|
|
redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, hpexpireat) {
|
|
REDIS_PROCESS_KW_CMD("HPEXPIREAT", redis_hexpire_cmd,
|
|
redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, httl) {
|
|
REDIS_PROCESS_KW_CMD("HTTL", redis_httl_cmd,
|
|
redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, hpttl) {
|
|
REDIS_PROCESS_KW_CMD("HPTTL", redis_httl_cmd,
|
|
redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, hexpiretime) {
|
|
REDIS_PROCESS_KW_CMD("HEXPIRETIME", redis_httl_cmd,
|
|
redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, hpexpiretime) {
|
|
REDIS_PROCESS_KW_CMD("HPEXPIRETIME", redis_httl_cmd,
|
|
redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, hpersist) {
|
|
REDIS_PROCESS_KW_CMD("HPERSIST", redis_httl_cmd,
|
|
redis_read_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto bool Redis::hRandField(string key, [array $options]) */
|
|
PHP_METHOD(Redis, hRandField)
|
|
{
|
|
REDIS_PROCESS_CMD(hrandfield, redis_hrandfield_response);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto long Redis::hstrlen(string key, string field) */
|
|
PHP_METHOD(Redis, hStrLen) {
|
|
REDIS_PROCESS_CMD(hstrlen, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* flag : get, set {ATOMIC, MULTI, PIPELINE} */
|
|
|
|
PHP_METHOD(Redis, multi)
|
|
{
|
|
|
|
RedisSock *redis_sock;
|
|
char *resp;
|
|
int resp_len;
|
|
zval *object;
|
|
zend_long multi_value = MULTI;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"O|l", &object, redis_ce, &multi_value)
|
|
== FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* if the flag is activated, send the command, the reply will be "QUEUED"
|
|
* or -ERR */
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (multi_value == PIPELINE) {
|
|
/* Cannot enter pipeline mode in a MULTI block */
|
|
if (IS_MULTI(redis_sock)) {
|
|
php_error_docref(NULL, E_ERROR, "Can't activate pipeline in multi mode!");
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Enable PIPELINE if we're not already in one */
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_sock->mode |= PIPELINE;
|
|
}
|
|
} else if (multi_value == MULTI) {
|
|
/* Don't want to do anything if we're already in MULTI mode */
|
|
if (!IS_MULTI(redis_sock)) {
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
pipeline_enqueue_cmd_strl(redis_sock, ZEND_STRL(RESP_MULTI_CMD));
|
|
redis_save_callback(redis_sock, NULL, redis_empty_ctx);
|
|
redis_sock->mode |= MULTI;
|
|
} else {
|
|
if (redis_sock_write(redis_sock, ZEND_STRL(RESP_MULTI_CMD)) < 0) {
|
|
RETURN_FALSE;
|
|
}
|
|
if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) {
|
|
RETURN_FALSE;
|
|
} else if (redis_strncmp(resp, ZEND_STRL("+OK")) != 0) {
|
|
efree(resp);
|
|
RETURN_FALSE;
|
|
}
|
|
efree(resp);
|
|
redis_sock->mode |= MULTI;
|
|
}
|
|
}
|
|
} else {
|
|
php_error_docref(NULL, E_WARNING, "Unknown mode sent to Redis::multi");
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
RETURN_ZVAL(getThis(), 1, 0);
|
|
}
|
|
|
|
/* discard */
|
|
PHP_METHOD(Redis, discard)
|
|
{
|
|
int ret = FAILURE;
|
|
RedisSock *redis_sock;
|
|
zval *object;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
|
|
&object, redis_ce) == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
ret = SUCCESS;
|
|
smart_string_free(&redis_sock->pipeline_cmd);
|
|
} else if (IS_MULTI(redis_sock)) {
|
|
ret = redis_send_discard(redis_sock);
|
|
}
|
|
if (ret == SUCCESS) {
|
|
redis_free_reply_callbacks(redis_sock);
|
|
redis_sock->mode = ATOMIC;
|
|
RETURN_TRUE;
|
|
}
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
PHP_REDIS_API int
|
|
redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS,
|
|
RedisSock *redis_sock, zval *z_tab)
|
|
{
|
|
|
|
char inbuf[4096];
|
|
size_t len;
|
|
|
|
if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
|
|
*inbuf != TYPE_MULTIBULK || atoi(inbuf + 1) < 0
|
|
) {
|
|
return FAILURE;
|
|
}
|
|
|
|
// No command issued, return empty immutable array
|
|
if (redis_sock->reply_callback == NULL) {
|
|
ZVAL_EMPTY_ARRAY(z_tab);
|
|
return SUCCESS;
|
|
}
|
|
|
|
array_init(z_tab);
|
|
|
|
return redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock, z_tab);
|
|
}
|
|
|
|
|
|
/* exec */
|
|
PHP_METHOD(Redis, exec)
|
|
{
|
|
RedisSock *redis_sock;
|
|
int ret;
|
|
zval *object, z_ret;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"O", &object, redis_ce) == FAILURE ||
|
|
(redis_sock = redis_sock_get(object, 0)) == NULL
|
|
) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
ZVAL_FALSE(&z_ret);
|
|
|
|
if (IS_MULTI(redis_sock)) {
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
pipeline_enqueue_cmd_strl(redis_sock, ZEND_STRL(RESP_EXEC_CMD));
|
|
redis_save_callback(redis_sock, NULL, redis_empty_ctx);
|
|
redis_sock->mode &= ~MULTI;
|
|
RETURN_ZVAL(getThis(), 1, 0);
|
|
}
|
|
if (redis_sock_write(redis_sock, ZEND_STRL(RESP_EXEC_CMD)) < 0) {
|
|
RETURN_FALSE;
|
|
}
|
|
ret = redis_sock_read_multibulk_multi_reply(
|
|
INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret);
|
|
redis_free_reply_callbacks(redis_sock);
|
|
redis_sock->mode &= ~MULTI;
|
|
redis_sock->watching = 0;
|
|
if (ret < 0) {
|
|
zval_ptr_dtor_nogc(&z_ret);
|
|
ZVAL_FALSE(&z_ret);
|
|
}
|
|
}
|
|
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
if (redis_sock->pipeline_cmd.len == 0) {
|
|
/* Empty array when no command was run. */
|
|
ZVAL_EMPTY_ARRAY(&z_ret);
|
|
} else {
|
|
if (redis_sock_write(redis_sock, redis_sock->pipeline_cmd.c,
|
|
redis_sock->pipeline_cmd.len) < 0) {
|
|
ZVAL_FALSE(&z_ret);
|
|
} else {
|
|
array_init(&z_ret);
|
|
if (redis_sock_read_multibulk_multi_reply_loop(
|
|
INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret) != SUCCESS) {
|
|
zval_ptr_dtor_nogc(&z_ret);
|
|
ZVAL_FALSE(&z_ret);
|
|
}
|
|
}
|
|
smart_string_free(&redis_sock->pipeline_cmd);
|
|
}
|
|
redis_free_reply_callbacks(redis_sock);
|
|
redis_sock->mode &= ~PIPELINE;
|
|
}
|
|
RETURN_ZVAL(&z_ret, 0, 1);
|
|
}
|
|
|
|
PHP_REDIS_API int
|
|
redis_response_enqueued(RedisSock *redis_sock)
|
|
{
|
|
char *resp;
|
|
int resp_len, ret = FAILURE;
|
|
|
|
if ((resp = redis_sock_read(redis_sock, &resp_len)) != NULL) {
|
|
if (redis_strncmp(resp, ZEND_STRL("+QUEUED")) == 0) {
|
|
ret = SUCCESS;
|
|
}
|
|
efree(resp);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
PHP_REDIS_API int
|
|
redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
|
|
RedisSock *redis_sock, zval *z_tab)
|
|
{
|
|
fold_item *fi;
|
|
uint8_t flags;
|
|
size_t i;
|
|
|
|
flags = redis_sock->flags;
|
|
for (i = 0; i < redis_sock->reply_callback_count; i++) {
|
|
fi = &redis_sock->reply_callback[i];
|
|
if (fi->fun) {
|
|
redis_sock->flags = fi->flags;
|
|
fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, fi->ctx);
|
|
redis_sock->flags = flags;
|
|
continue;
|
|
}
|
|
size_t len;
|
|
char inbuf[255];
|
|
|
|
if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
|
|
redis_strncmp(inbuf, ZEND_STRL("+OK")) != 0)
|
|
{
|
|
return FAILURE;
|
|
}
|
|
|
|
while (redis_sock->reply_callback[++i].fun) {
|
|
if (redis_response_enqueued(redis_sock) != SUCCESS) {
|
|
return FAILURE;
|
|
}
|
|
}
|
|
|
|
if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
|
|
return FAILURE;
|
|
}
|
|
|
|
zval z_ret;
|
|
array_init(&z_ret);
|
|
add_next_index_zval(z_tab, &z_ret);
|
|
|
|
int num = atol(inbuf + 1);
|
|
|
|
if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) != SUCCESS) {
|
|
return FAILURE;
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
PHP_METHOD(Redis, pipeline)
|
|
{
|
|
RedisSock *redis_sock;
|
|
zval *object;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"O", &object, redis_ce) == FAILURE ||
|
|
(redis_sock = redis_sock_get(object, 0)) == NULL
|
|
) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* User cannot enter MULTI mode if already in a pipeline */
|
|
if (IS_MULTI(redis_sock)) {
|
|
php_error_docref(NULL, E_ERROR, "Can't activate pipeline in multi mode!");
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Enable pipeline mode unless we're already in that mode in which case this
|
|
* is just a NO OP */
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_sock->mode |= PIPELINE;
|
|
}
|
|
|
|
RETURN_ZVAL(getThis(), 1, 0);
|
|
}
|
|
|
|
/* {{{ proto long Redis::publish(string channel, string msg) */
|
|
PHP_METHOD(Redis, publish)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("PUBLISH", redis_key_str_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void Redis::psubscribe([pattern1, pattern2, ... patternN]) */
|
|
PHP_METHOD(Redis, psubscribe)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd,
|
|
redis_subscribe_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void Redis::ssubscribe([shardchannel1, shardchannel2, ... shardchannelN]) */
|
|
PHP_METHOD(Redis, ssubscribe)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SSUBSCRIBE", redis_subscribe_cmd,
|
|
redis_subscribe_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void Redis::subscribe([channel1, channel2, ... channelN]) */
|
|
PHP_METHOD(Redis, subscribe) {
|
|
REDIS_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd,
|
|
redis_subscribe_response);
|
|
}
|
|
|
|
/**
|
|
* [ps]unsubscribe channel_0 channel_1 ... channel_n
|
|
* [ps]unsubscribe([channel_0, channel_1, ..., channel_n])
|
|
* response format :
|
|
* array(
|
|
* channel_0 => TRUE|FALSE,
|
|
* channel_1 => TRUE|FALSE,
|
|
* ...
|
|
* channel_n => TRUE|FALSE
|
|
* );
|
|
**/
|
|
|
|
PHP_METHOD(Redis, unsubscribe)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("UNSUBSCRIBE", redis_unsubscribe_cmd,
|
|
redis_unsubscribe_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, punsubscribe)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("PUNSUBSCRIBE", redis_unsubscribe_cmd,
|
|
redis_unsubscribe_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, sunsubscribe)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SUNSUBSCRIBE", redis_unsubscribe_cmd,
|
|
redis_unsubscribe_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, waitaof) {
|
|
REDIS_PROCESS_CMD(waitaof, redis_read_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto string Redis::bgrewriteaof() */
|
|
PHP_METHOD(Redis, bgrewriteaof)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("BGREWRITEAOF", redis_empty_cmd,
|
|
redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ public function slaveof(string $host = NULL, int $port = NULL): Redis|bool }}} */
|
|
PHP_METHOD(Redis, slaveof) {
|
|
REDIS_PROCESS_KW_CMD("SLAVEOF", redis_replicaof_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ public function replicaof(string $host = NULL, int $port = NULL): Redis|bool }}} */
|
|
PHP_METHOD(Redis, replicaof) {
|
|
REDIS_PROCESS_KW_CMD("REPLICAOF", redis_replicaof_cmd, redis_boolean_response);
|
|
}
|
|
|
|
/* }}} */
|
|
/* {{{ proto string Redis::object(key) */
|
|
PHP_METHOD(Redis, object)
|
|
{
|
|
REDIS_PROCESS_CMD(object, redis_object_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::getOption($option) */
|
|
PHP_METHOD(Redis, getOption)
|
|
{
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::setOption(string $option, mixed $value) */
|
|
PHP_METHOD(Redis, setOption)
|
|
{
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::config(string op, string key [, mixed value]) */
|
|
/* {{{ proto public function config(string $op, string ...$args) }}} */
|
|
// CONFIG SET/GET
|
|
PHP_METHOD(Redis, config) {
|
|
REDIS_PROCESS_CMD(config, redis_config_response);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto boolean Redis::slowlog(string arg, [int option]) */
|
|
PHP_METHOD(Redis, slowlog) {
|
|
REDIS_PROCESS_CMD(slowlog, redis_read_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto Redis::wait(int num_slaves, int ms) }}} */
|
|
PHP_METHOD(Redis, wait) {
|
|
REDIS_PROCESS_KW_CMD("WAIT", redis_long_long_cmd, redis_long_response);
|
|
}
|
|
|
|
/*
|
|
* {{{ proto Redis::pubsub("channels", pattern);
|
|
* proto Redis::pubsub("numsub", Array channels);
|
|
* proto Redis::pubsub("numpat"); }}}
|
|
*/
|
|
PHP_METHOD(Redis, pubsub) {
|
|
REDIS_PROCESS_CMD(pubsub, redis_pubsub_response);
|
|
}
|
|
|
|
/* {{{ proto variant Redis::eval(string script, [array keys, long num_keys]) */
|
|
PHP_METHOD(Redis, eval) {
|
|
REDIS_PROCESS_KW_CMD("EVAL", redis_eval_cmd, redis_read_raw_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto variant Redis::eval_ro(string script, [array keys, long num_keys]) */
|
|
PHP_METHOD(Redis, eval_ro) {
|
|
REDIS_PROCESS_KW_CMD("EVAL_RO", redis_eval_cmd, redis_read_raw_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto variant Redis::evalsha(string sha1, [array keys, long num_keys]) */
|
|
PHP_METHOD(Redis, evalsha) {
|
|
REDIS_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, redis_read_raw_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto variant Redis::evalsha_ro(string sha1, [array keys, long num_keys]) */
|
|
PHP_METHOD(Redis, evalsha_ro) {
|
|
REDIS_PROCESS_KW_CMD("EVALSHA_RO", redis_eval_cmd, redis_read_raw_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto variant Redis::fcall(string fn [, array keys [, array args]]) */
|
|
PHP_METHOD(Redis, fcall) {
|
|
REDIS_PROCESS_KW_CMD("FCALL", redis_fcall_cmd, redis_read_raw_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto variant Redis::fcall_ro(string fn [, array keys [, array args]]) */
|
|
PHP_METHOD(Redis, fcall_ro) {
|
|
REDIS_PROCESS_KW_CMD("FCALL_RO", redis_fcall_cmd, redis_read_raw_variant_reply);
|
|
}
|
|
|
|
/* {{{ public function script($args...): mixed }}} */
|
|
PHP_METHOD(Redis, script) {
|
|
REDIS_PROCESS_CMD(script, redis_read_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto DUMP key */
|
|
PHP_METHOD(Redis, dump) {
|
|
REDIS_PROCESS_KW_CMD("DUMP", redis_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::restore(ttl, key, value) */
|
|
PHP_METHOD(Redis, restore) {
|
|
REDIS_PROCESS_CMD(restore, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::debug(string key) */
|
|
PHP_METHOD(Redis, debug) {
|
|
REDIS_PROCESS_KW_CMD("DEBUG", redis_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::migrate(host port key dest-db timeout [bool copy,
|
|
* bool replace]) */
|
|
PHP_METHOD(Redis, migrate) {
|
|
REDIS_PROCESS_CMD(migrate, redis_boolean_response);
|
|
}
|
|
|
|
/* {{{ proto Redis::_prefix(key) */
|
|
PHP_METHOD(Redis, _prefix) {
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
|
|
}
|
|
|
|
/* {{{ proto Redis::_serialize(value) */
|
|
PHP_METHOD(Redis, _serialize) {
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_serialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
|
|
}
|
|
|
|
/* {{{ proto Redis::_unserialize(value) */
|
|
PHP_METHOD(Redis, _unserialize) {
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_unserialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
|
|
redis_exception_ce);
|
|
}
|
|
|
|
PHP_METHOD(Redis, _compress) {
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_compress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
|
|
}
|
|
|
|
PHP_METHOD(Redis, _uncompress) {
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_uncompress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
|
|
redis_exception_ce);
|
|
}
|
|
|
|
PHP_METHOD(Redis, _pack) {
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_pack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
|
|
}
|
|
|
|
PHP_METHOD(Redis, _digest) {
|
|
RedisSock *redis_sock;
|
|
|
|
redis_sock = redis_sock_get_instance(getThis(), 0);
|
|
if (redis_sock == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_digest_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
|
|
redis_exception_ce);
|
|
}
|
|
|
|
PHP_METHOD(Redis, _unpack) {
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_unpack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
|
|
}
|
|
|
|
/* {{{ proto Redis::getLastError() */
|
|
PHP_METHOD(Redis, getLastError) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab our object
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
|
|
&object, redis_ce) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Return our last error or NULL if we don't have one */
|
|
if (redis_sock->err) {
|
|
RETURN_STRINGL(ZSTR_VAL(redis_sock->err), ZSTR_LEN(redis_sock->err));
|
|
}
|
|
RETURN_NULL();
|
|
}
|
|
|
|
/* {{{ proto Redis::clearLastError() */
|
|
PHP_METHOD(Redis, clearLastError) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab our object
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
|
|
&object, redis_ce) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_sock_clear_err(redis_sock);
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
|
|
/*
|
|
* {{{ proto long Redis::getMode()
|
|
*/
|
|
PHP_METHOD(Redis, getMode) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
|
|
/* Grab our object */
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Grab socket */
|
|
if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
RETVAL_LONG(PIPELINE);
|
|
} else if (IS_MULTI(redis_sock)) {
|
|
RETVAL_LONG(MULTI);
|
|
} else {
|
|
RETVAL_LONG(ATOMIC);
|
|
}
|
|
}
|
|
|
|
/* {{{ proto Redis::time() */
|
|
PHP_METHOD(Redis, time) {
|
|
REDIS_PROCESS_KW_CMD("TIME", redis_empty_cmd, redis_mbulk_reply_raw);
|
|
}
|
|
|
|
/* {{{ proto array Redis::role() */
|
|
PHP_METHOD(Redis, role) {
|
|
REDIS_PROCESS_KW_CMD("ROLE", redis_empty_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
/*
|
|
* Introspection stuff
|
|
*/
|
|
|
|
/* {{{ proto Redis::IsConnected */
|
|
PHP_METHOD(Redis, isConnected) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
|
|
/* Grab our object */
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Grab socket */
|
|
if ((redis_sock = redis_sock_get_instance(object, 1)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
RETURN_BOOL(redis_sock->status >= REDIS_SOCK_STATUS_CONNECTED);
|
|
}
|
|
|
|
/* {{{ proto Redis::getHost() */
|
|
PHP_METHOD(Redis, getHost) {
|
|
RedisSock *redis_sock;
|
|
|
|
if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
|
|
RETURN_STRINGL(ZSTR_VAL(redis_sock->host), ZSTR_LEN(redis_sock->host));
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
/* {{{ proto Redis::getPort() */
|
|
PHP_METHOD(Redis, getPort) {
|
|
RedisSock *redis_sock;
|
|
|
|
if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
|
|
/* Return our port */
|
|
RETURN_LONG(redis_sock->port);
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
PHP_METHOD(Redis, serverName) {
|
|
RedisSock *rs;
|
|
|
|
if ((rs = redis_sock_get_instance(getThis(), 1)) == NULL) {
|
|
RETURN_FALSE;
|
|
} else if (!IS_ATOMIC(rs)) {
|
|
php_error_docref(NULL, E_ERROR,
|
|
"Can't call serverName in multi or pipeline mode!");
|
|
RETURN_FALSE;
|
|
} else if (rs->hello.server != NULL) {
|
|
RETURN_STR_COPY(rs->hello.server);
|
|
}
|
|
|
|
REDIS_PROCESS_KW_CMD("HELLO", redis_empty_cmd, redis_hello_server_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, serverVersion) {
|
|
RedisSock *rs;
|
|
|
|
if ((rs = redis_sock_get_instance(getThis(), 1)) == NULL) {
|
|
RETURN_FALSE;
|
|
} else if (!IS_ATOMIC(rs)) {
|
|
php_error_docref(NULL, E_ERROR,
|
|
"Can't call serverVersion in multi or pipeline mode!");
|
|
RETURN_FALSE;
|
|
} else if (rs->hello.version != NULL) {
|
|
RETURN_STR_COPY(rs->hello.version);
|
|
}
|
|
|
|
REDIS_PROCESS_KW_CMD("HELLO", redis_empty_cmd, redis_hello_version_response);
|
|
}
|
|
|
|
/* {{{ proto Redis::getDBNum */
|
|
PHP_METHOD(Redis, getDBNum) {
|
|
RedisSock *redis_sock;
|
|
|
|
if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
|
|
/* Return our db number */
|
|
RETURN_LONG(redis_sock->dbNumber);
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
PHP_METHOD(Redis, getTransferredBytes) {
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
array_init_size(return_value, 2);
|
|
add_next_index_long(return_value, redis_sock->txBytes);
|
|
add_next_index_long(return_value, redis_sock->rxBytes);
|
|
}
|
|
|
|
PHP_METHOD(Redis, clearTransferredBytes) {
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
redis_sock->txBytes = 0;
|
|
redis_sock->rxBytes = 0;
|
|
}
|
|
|
|
/* {{{ proto Redis::getTimeout */
|
|
PHP_METHOD(Redis, getTimeout) {
|
|
RedisSock *redis_sock;
|
|
|
|
if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
|
|
RETURN_DOUBLE(redis_sock->timeout);
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
/* {{{ proto Redis::getReadTimeout */
|
|
PHP_METHOD(Redis, getReadTimeout) {
|
|
RedisSock *redis_sock;
|
|
|
|
if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
|
|
RETURN_DOUBLE(redis_sock->read_timeout);
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
/* {{{ proto Redis::getPersistentID */
|
|
PHP_METHOD(Redis, getPersistentID) {
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) {
|
|
RETURN_FALSE;
|
|
} else if (redis_sock->persistent_id == NULL) {
|
|
RETURN_NULL();
|
|
}
|
|
RETURN_STRINGL(ZSTR_VAL(redis_sock->persistent_id), ZSTR_LEN(redis_sock->persistent_id));
|
|
}
|
|
|
|
/* {{{ proto Redis::getAuth */
|
|
PHP_METHOD(Redis, getAuth) {
|
|
RedisSock *redis_sock;
|
|
zval zret;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU);
|
|
if (redis_sock == NULL)
|
|
RETURN_FALSE;
|
|
|
|
if (redis_sock->user && redis_sock->pass) {
|
|
array_init(&zret);
|
|
add_next_index_str(&zret, zend_string_copy(redis_sock->user));
|
|
add_next_index_str(&zret, zend_string_copy(redis_sock->pass));
|
|
RETURN_ZVAL(&zret, 0, 0);
|
|
} else if (redis_sock->pass) {
|
|
RETURN_STR_COPY(redis_sock->pass);
|
|
} else {
|
|
RETURN_NULL();
|
|
}
|
|
}
|
|
|
|
/* {{{ proto mixed Redis::client(string $command, [ $arg1 ... $argN]) */
|
|
PHP_METHOD(Redis, client) {
|
|
REDIS_PROCESS_CMD(client, redis_client_response);
|
|
}
|
|
/* }}} */
|
|
|
|
static RedisCmd *
|
|
redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
|
|
zval *argv;
|
|
int argc;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(1, -1)
|
|
Z_PARAM_VARIADIC('+', argv, argc)
|
|
ZEND_PARSE_PARAMETERS_END_EX(return NULL);
|
|
|
|
return redis_build_raw_cmd(argv, argc);
|
|
}
|
|
|
|
PHP_METHOD(Redis, rawcommand) {
|
|
REDIS_PROCESS_CMD(rawcommand, redis_read_raw_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto array Redis::command()
|
|
* proto array Redis::command('info', string cmd)
|
|
* proto array Redis::command('getkeys', array cmd_args) */
|
|
PHP_METHOD(Redis, command) {
|
|
REDIS_PROCESS_CMD(command, redis_command_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::copy(string $source, string $destination, array $options = null) */
|
|
PHP_METHOD(Redis, copy) {
|
|
REDIS_PROCESS_CMD(copy, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* Helper to format any combination of SCAN arguments */
|
|
static RedisCmd *
|
|
redis_build_scan_cmd(REDIS_SCAN_TYPE type, zend_string *key, uint64_t cursor,
|
|
zend_string *pattern, int count, zend_string *match_type)
|
|
{
|
|
RedisCmd *cmd;
|
|
char *keyword;
|
|
|
|
/* Turn our type into a keyword */
|
|
switch(type) {
|
|
case TYPE_SCAN:
|
|
keyword = "SCAN";
|
|
break;
|
|
case TYPE_SSCAN:
|
|
keyword = "SSCAN";
|
|
break;
|
|
case TYPE_HSCAN:
|
|
keyword = "HSCAN";
|
|
break;
|
|
case TYPE_ZSCAN:
|
|
default:
|
|
keyword = "ZSCAN";
|
|
break;
|
|
}
|
|
|
|
|
|
/* Start the command */
|
|
cmd = redis_cmd_create(NULL, keyword, strlen(keyword));
|
|
if (key && ZSTR_LEN(key) > 0) redis_cmd_cat_zstr(cmd, key);
|
|
redis_cmd_cat_u64(cmd, cursor);
|
|
|
|
/* Append COUNT if we've got it */
|
|
if(count) {
|
|
redis_cmd_cat_literal(cmd, "COUNT");
|
|
redis_cmd_cat_long(cmd, count);
|
|
}
|
|
|
|
/* Append MATCH if we've got it */
|
|
if(pattern && ZSTR_LEN(pattern) > 0) {
|
|
redis_cmd_cat_literal(cmd, "MATCH");
|
|
redis_cmd_cat_zstr(cmd, pattern);
|
|
}
|
|
|
|
if (match_type) {
|
|
redis_cmd_cat_literal(cmd, "TYPE");
|
|
redis_cmd_cat_zstr(cmd, match_type);
|
|
}
|
|
|
|
return cmd;
|
|
}
|
|
|
|
/* {{{ proto redis::scan(&$cursor, [pattern, [count, [type]]]) */
|
|
PHP_REDIS_API void
|
|
generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
|
|
zend_string *key = NULL, *pattern = NULL;
|
|
zend_string *match_type = NULL;
|
|
zval *object, *z_cursor;
|
|
RedisSock *redis_sock;
|
|
zend_bool pattern_free = 0;
|
|
zend_long count = 0;
|
|
zend_bool completed;
|
|
HashTable *hash;
|
|
int num_elements;
|
|
uint64_t cursor;
|
|
RedisCmd *cmd;
|
|
|
|
/* Different prototype depending on if this is a key based scan */
|
|
if(type != TYPE_SCAN) {
|
|
// Requires a key
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"OS!z/|S!l", &object, redis_ce, &key,
|
|
&z_cursor, &pattern, &count)==FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
} else {
|
|
// Doesn't require a key
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"Oz/|S!lS!", &object, redis_ce, &z_cursor,
|
|
&pattern, &count, &match_type) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
/* Grab our socket */
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Calling this in a pipeline makes no sense */
|
|
if (!IS_ATOMIC(redis_sock)) {
|
|
php_error_docref(NULL, E_ERROR,
|
|
"Can't call SCAN commands in multi or pipeline mode!");
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Get our SCAN cursor short circuiting if we're done */
|
|
cursor = redisGetScanCursor(z_cursor, &completed);
|
|
if (completed)
|
|
RETURN_FALSE;
|
|
|
|
if(key)
|
|
key = redis_key_prefix_zstr(redis_sock, key);
|
|
|
|
if (pattern && redis_sock->scan & REDIS_SCAN_PREFIX) {
|
|
pattern = redis_key_prefix_zstr(redis_sock, pattern);
|
|
pattern_free = 1;
|
|
}
|
|
|
|
/**
|
|
* Redis can return to us empty keys, especially in the case where there
|
|
* are a large number of keys to scan, and we're matching against a
|
|
* pattern. phpredis can be set up to abstract this from the user, by
|
|
* setting OPT_SCAN to REDIS_SCAN_RETRY. Otherwise we will return empty
|
|
* keys and the user will need to make subsequent calls with an updated
|
|
* cursor.
|
|
*/
|
|
do {
|
|
/* Free our previous reply if we're back in the loop. We know we are
|
|
* if our return_value is an array */
|
|
if (Z_TYPE_P(return_value) == IS_ARRAY) {
|
|
zval_ptr_dtor_nogc(return_value);
|
|
ZVAL_NULL(return_value);
|
|
}
|
|
|
|
// Format our SCAN command
|
|
cmd = redis_build_scan_cmd(type, key, cursor, pattern, count, match_type);
|
|
|
|
if (redis_process_request(redis_sock, cmd) != SUCCESS ||
|
|
redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock, type, &cursor) < 0)
|
|
{
|
|
if (key)
|
|
zend_string_release(key);
|
|
if (pattern_free)
|
|
zend_string_release(pattern);
|
|
redis_cmd_free(cmd);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Get the number of elements */
|
|
hash = Z_ARRVAL_P(return_value);
|
|
num_elements = zend_hash_num_elements(hash);
|
|
|
|
redis_cmd_free(cmd);
|
|
} while (redis_sock->scan & REDIS_SCAN_RETRY && cursor != 0 &&
|
|
num_elements == 0);
|
|
|
|
if (pattern_free)
|
|
zend_string_release(pattern);
|
|
if(key)
|
|
zend_string_release(key);
|
|
|
|
/* Update our cursor reference */
|
|
redisSetScanCursor(z_cursor, cursor);
|
|
}
|
|
|
|
PHP_METHOD(Redis, scan) {
|
|
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SCAN);
|
|
}
|
|
PHP_METHOD(Redis, hscan) {
|
|
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_HSCAN);
|
|
}
|
|
PHP_METHOD(Redis, sscan) {
|
|
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SSCAN);
|
|
}
|
|
PHP_METHOD(Redis, zscan) {
|
|
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN);
|
|
}
|
|
|
|
/*
|
|
* HyperLogLog based commands
|
|
*/
|
|
|
|
/* {{{ proto Redis::pfAdd(string key, array elements) }}} */
|
|
PHP_METHOD(Redis, pfadd) {
|
|
REDIS_PROCESS_CMD(pfadd, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto Redis::pfCount(string key) }}}*/
|
|
PHP_METHOD(Redis, pfcount) {
|
|
REDIS_PROCESS_CMD(pfcount, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto Redis::pfMerge(string dstkey, array keys) }}}*/
|
|
PHP_METHOD(Redis, pfmerge) {
|
|
REDIS_PROCESS_CMD(pfmerge, redis_boolean_response);
|
|
}
|
|
|
|
/*
|
|
* Geo commands
|
|
*/
|
|
|
|
PHP_METHOD(Redis, geoadd) {
|
|
REDIS_PROCESS_CMD(geoadd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, geohash) {
|
|
REDIS_PROCESS_KW_CMD("GEOHASH", redis_key_varval_cmd, redis_mbulk_reply_raw);
|
|
}
|
|
|
|
PHP_METHOD(Redis, geopos) {
|
|
REDIS_PROCESS_KW_CMD("GEOPOS", redis_key_varval_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, geodist) {
|
|
REDIS_PROCESS_CMD(geodist, redis_bulk_double_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, georadius) {
|
|
REDIS_PROCESS_KW_CMD("GEORADIUS", redis_georadius_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, georadius_ro) {
|
|
REDIS_PROCESS_KW_CMD("GEORADIUS_RO", redis_georadius_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, georadiusbymember) {
|
|
REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER", redis_georadiusbymember_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, georadiusbymember_ro) {
|
|
REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, geosearch) {
|
|
REDIS_PROCESS_CMD(geosearch, redis_geosearch_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, geosearchstore) {
|
|
REDIS_PROCESS_CMD(geosearchstore, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, digest) {
|
|
REDIS_PROCESS_KW_CMD("DIGEST", redis_key_cmd, redis_ping_response);
|
|
}
|
|
|
|
/*
|
|
* Vectors
|
|
*/
|
|
|
|
PHP_METHOD(Redis, vadd) {
|
|
REDIS_PROCESS_CMD(vadd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vsim) {
|
|
REDIS_PROCESS_CMD(vsim, redis_zrange_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vcard) {
|
|
REDIS_PROCESS_KW_CMD("VCARD", redis_key_cmd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vdim) {
|
|
REDIS_PROCESS_KW_CMD("VDIM", redis_key_cmd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vinfo) {
|
|
REDIS_PROCESS_KW_CMD("VINFO", redis_key_cmd, redis_vinfo_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vismember) {
|
|
REDIS_PROCESS_KW_CMD("VISMEMBER", redis_kv_cmd, redis_1_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vemb) {
|
|
REDIS_PROCESS_CMD(vemb, redis_vemb_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vrandmember) {
|
|
REDIS_PROCESS_KW_CMD("VRANDMEMBER", redis_randmember_cmd,
|
|
redis_randmember_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vrange) {
|
|
REDIS_PROCESS_KW_CMD("VRANGE", redis_vrange_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vrem) {
|
|
REDIS_PROCESS_KW_CMD("VREM", redis_kv_cmd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vgetattr) {
|
|
REDIS_PROCESS_CMD(vgetattr, redis_vgetattr_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vsetattr) {
|
|
REDIS_PROCESS_CMD(vsetattr, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, vlinks) {
|
|
REDIS_PROCESS_CMD(vlinks, redis_vlinks_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, gcra) {
|
|
REDIS_PROCESS_CMD(gcra, redis_read_variant_reply);
|
|
}
|
|
|
|
/*
|
|
* Streams
|
|
*/
|
|
|
|
PHP_METHOD(Redis, xack) {
|
|
REDIS_PROCESS_CMD(xack, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xadd) {
|
|
REDIS_PROCESS_CMD(xadd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xautoclaim) {
|
|
REDIS_PROCESS_CMD(xautoclaim, redis_xclaim_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xclaim) {
|
|
REDIS_PROCESS_CMD(xclaim, redis_xclaim_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xdel) {
|
|
REDIS_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xdelex) {
|
|
REDIS_PROCESS_CMD(xdelex, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xgroup) {
|
|
REDIS_PROCESS_CMD(xgroup, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xinfo) {
|
|
REDIS_PROCESS_CMD(xinfo, redis_xinfo_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xlen) {
|
|
REDIS_PROCESS_KW_CMD("XLEN", redis_key_cmd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xpending) {
|
|
REDIS_PROCESS_CMD(xpending, redis_read_variant_reply_strings);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xrange) {
|
|
REDIS_PROCESS_KW_CMD("XRANGE", redis_xrange_cmd, redis_xrange_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xread) {
|
|
REDIS_PROCESS_CMD(xread, redis_xread_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xreadgroup) {
|
|
REDIS_PROCESS_CMD(xreadgroup, redis_xread_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xrevrange) {
|
|
REDIS_PROCESS_KW_CMD("XREVRANGE", redis_xrange_cmd, redis_xrange_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xtrim) {
|
|
REDIS_PROCESS_CMD(xtrim, redis_long_response);
|
|
}
|
|
|
|
/* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */
|