Files
phpredis/redis.c
michael-grunder b144743345 Rework the HMGET command to skip invalid keys
This relates to a previous hotfix for issue #379 where phpredis
would time out if you sent an array of empty values.  The reason
it was timing out is that the argument count being sent wasn't
reflecting any skipped items in the array (meaning redis was
waiting for the rest of the command).

I realized that the previous fix would still fail if you were to
send some valid values, with invalid (null, empty string, etc)
ones mixed in.

Presently, we're just skipping invalid items in the array but there
might be a case to issue a php_error_docref type warning when we
encounter them, so the user can know that it happened.

In addition, HMGET now uses a smart_str to build the command, which
means the time it takes to build the key will scale in a linear fashion
2013-09-05 15:08:30 -07:00

6633 lines
196 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| 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 "common.h"
#include "ext/standard/info.h"
#include "php_ini.h"
#include "php_redis.h"
#include "redis_array.h"
#include <zend_exceptions.h>
#ifdef PHP_SESSION
#include "ext/session/php_session.h"
#endif
#include <ext/standard/php_smart_str.h>
#include <ext/standard/php_var.h>
#include <ext/standard/php_math.h>
#include "library.h"
#define R_SUB_CALLBACK_CLASS_TYPE 1
#define R_SUB_CALLBACK_FT_TYPE 2
#define R_SUB_CLOSURE_TYPE 3
int le_redis_sock;
extern int le_redis_array;
#ifdef PHP_SESSION
extern ps_module ps_mod_redis;
#endif
extern zend_class_entry *redis_array_ce;
zend_class_entry *redis_ce;
zend_class_entry *redis_exception_ce;
zend_class_entry *spl_ce_RuntimeException = NULL;
extern zend_function_entry redis_array_functions[];
PHP_INI_BEGIN()
/* redis arrays */
PHP_INI_ENTRY("redis.arrays.names", "", PHP_INI_ALL, NULL)
PHP_INI_ENTRY("redis.arrays.hosts", "", PHP_INI_ALL, NULL)
PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL)
PHP_INI_ENTRY("redis.arrays.functions", "", PHP_INI_ALL, NULL)
PHP_INI_ENTRY("redis.arrays.index", "", PHP_INI_ALL, NULL)
PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL)
PHP_INI_END()
ZEND_DECLARE_MODULE_GLOBALS(redis)
static zend_function_entry redis_functions[] = {
PHP_ME(Redis, __construct, NULL, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC)
PHP_ME(Redis, __destruct, NULL, ZEND_ACC_DTOR | ZEND_ACC_PUBLIC)
PHP_ME(Redis, connect, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, pconnect, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, close, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, ping, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, echo, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, get, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, set, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, setex, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, psetex, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, setnx, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getSet, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, randomKey, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, renameKey, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, renameNx, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getMultiple, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, exists, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, delete, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, incr, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, incrBy, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, incrByFloat, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, decr, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, decrBy, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, type, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, append, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getRange, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, setRange, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getBit, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, setBit, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, strlen, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getKeys, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sort, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sortAsc, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sortAscAlpha, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sortDesc, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sortDescAlpha, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, lPush, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, rPush, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, lPushx, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, rPushx, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, lPop, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, rPop, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, blPop, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, brPop, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, lSize, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, lRemove, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, listTrim, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, lGet, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, lGetRange, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, lSet, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, lInsert, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sAdd, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sSize, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sRemove, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sMove, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sPop, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sRandMember, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sContains, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sMembers, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sInter, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sInterStore, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sUnion, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sUnionStore, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sDiff, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sDiffStore, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, setTimeout, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, save, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, bgSave, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, lastSave, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, flushDB, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, flushAll, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, dbSize, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, auth, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, ttl, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, pttl, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, persist, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, info, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, resetStat, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, select, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, move, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, bgrewriteaof, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, slaveof, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, object, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, bitop, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, bitcount, NULL, ZEND_ACC_PUBLIC)
/* 1.1 */
PHP_ME(Redis, mset, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, msetnx, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, rpoplpush, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, brpoplpush, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zAdd, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zDelete, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zRange, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zReverseRange, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zRangeByScore, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zRevRangeByScore, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zCount, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zDeleteRangeByRank, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zCard, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zScore, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zRank, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zRevRank, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zInter, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zUnion, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zIncrBy, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, expireAt, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, pexpire, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, pexpireAt, NULL, ZEND_ACC_PUBLIC)
/* 1.2 */
PHP_ME(Redis, hGet, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hSet, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hSetNx, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hDel, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hLen, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hKeys, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hVals, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hGetAll, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hExists, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hIncrBy, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hIncrByFloat, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hMset, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hMget, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, multi, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, discard, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, exec, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, pipeline, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, watch, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, unwatch, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, publish, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, subscribe, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, psubscribe, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, unsubscribe, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, punsubscribe, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, time, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, eval, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, evalsha, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, script, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, dump, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, restore, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, migrate, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getLastError, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, clearLastError, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, _prefix, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC)
/* options */
PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, setOption, NULL, ZEND_ACC_PUBLIC)
/* config */
PHP_ME(Redis, config, NULL, ZEND_ACC_PUBLIC)
/* slowlog */
PHP_ME(Redis, slowlog, NULL, ZEND_ACC_PUBLIC)
/* introspection */
PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getPort, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getDBNum, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getTimeout, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getReadTimeout, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getPersistentID, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getAuth, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, isConnected, NULL, ZEND_ACC_PUBLIC)
/* aliases */
PHP_MALIAS(Redis, open, connect, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, popen, pconnect, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, lLen, lSize, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, sGetMembers, sMembers, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, mget, getMultiple, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, expire, setTimeout, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, zunionstore, zUnion, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, zinterstore, zInter, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, zRemove, zDelete, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, zRem, zDelete, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, zRemoveRangeByScore, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, zRemRangeByScore, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, zRemRangeByRank, zDeleteRangeByRank, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, zSize, zCard, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, substr, getRange, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, rename, renameKey, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, del, delete, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, keys, getKeys, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, lrem, lRemove, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, ltrim, listTrim, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, lindex, lGet, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, lrange, lGetRange, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, scard, sSize, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, srem, sRemove, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, sismember, sContains, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, zrevrange, zReverseRange, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, sendEcho, echo, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, evaluate, eval, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, evaluateSha, evalsha, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
zend_module_entry redis_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"redis",
NULL,
PHP_MINIT(redis),
PHP_MSHUTDOWN(redis),
PHP_RINIT(redis),
PHP_RSHUTDOWN(redis),
PHP_MINFO(redis),
#if ZEND_MODULE_API_NO >= 20010901
PHP_REDIS_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_REDIS
ZEND_GET_MODULE(redis)
#endif
PHPAPI zend_class_entry *redis_get_exception_base(int root TSRMLS_DC)
{
#if HAVE_SPL
if (!root) {
if (!spl_ce_RuntimeException) {
zend_class_entry **pce;
if (zend_hash_find(CG(class_table), "runtimeexception",
sizeof("RuntimeException"), (void **) &pce) == SUCCESS) {
spl_ce_RuntimeException = *pce;
return *pce;
}
} else {
return spl_ce_RuntimeException;
}
}
#endif
#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2)
return zend_exception_get_default();
#else
return zend_exception_get_default(TSRMLS_C);
#endif
}
/**
* Send a static DISCARD in case we're in MULTI mode.
*/
static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) {
int result = FAILURE;
char *cmd, *response;
int response_len, cmd_len;
/* format our discard command */
cmd_len = redis_cmd_format_static(&cmd, "DISCARD", "");
/* send our DISCARD command */
if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 &&
(response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) != NULL) {
/* success if we get OK */
result = (response_len == 3 && strncmp(response,"+OK", 3) == 0) ? SUCCESS : FAILURE;
/* free our response */
efree(response);
}
/* free our command */
efree(cmd);
/* return success/failure */
return result;
}
/**
* redis_destructor_redis_sock
*/
static void redis_destructor_redis_sock(zend_rsrc_list_entry * rsrc TSRMLS_DC)
{
RedisSock *redis_sock = (RedisSock *) rsrc->ptr;
redis_sock_disconnect(redis_sock TSRMLS_CC);
redis_free_socket(redis_sock);
}
/**
* redis_sock_get
*/
PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw)
{
zval **socket;
int resource_type;
if (Z_TYPE_P(id) != IS_OBJECT || zend_hash_find(Z_OBJPROP_P(id), "socket",
sizeof("socket"), (void **) &socket) == FAILURE) {
// Throw an exception unless we've been requested not to
if(!no_throw) {
zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC);
}
return -1;
}
*redis_sock = (RedisSock *) zend_list_find(Z_LVAL_PP(socket), &resource_type);
if (!*redis_sock || resource_type != le_redis_sock) {
// Throw an exception unless we've been requested not to
if(!no_throw) {
zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC);
}
return -1;
}
if ((*redis_sock)->lazy_connect)
{
(*redis_sock)->lazy_connect = 0;
if (redis_sock_server_open(*redis_sock, 1 TSRMLS_CC) < 0) {
return -1;
}
}
return Z_LVAL_PP(socket);
}
/**
* redis_sock_get_direct
* Returns our attached RedisSock pointer if we're connected
*/
PHPAPI 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() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) ||
(redis_sock_get(object, &redis_sock TSRMLS_CC, 1) < 0) || redis_sock->status != REDIS_SOCK_STATUS_CONNECTED)
{
return NULL;
}
// Return our socket
return redis_sock;
}
/**
* PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(redis)
{
zend_class_entry redis_class_entry;
zend_class_entry redis_array_class_entry;
zend_class_entry redis_exception_class_entry;
REGISTER_INI_ENTRIES();
/* Redis class */
INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions);
redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC);
/* RedisArray class */
INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions);
redis_array_ce = zend_register_internal_class(&redis_array_class_entry TSRMLS_CC);
le_redis_array = zend_register_list_destructors_ex(
redis_destructor_redis_array,
NULL,
"Redis Array", module_number
);
/* RedisException class */
INIT_CLASS_ENTRY(redis_exception_class_entry, "RedisException", NULL);
redis_exception_ce = zend_register_internal_class_ex(
&redis_exception_class_entry,
redis_get_exception_base(0 TSRMLS_CC),
NULL TSRMLS_CC
);
le_redis_sock = zend_register_list_destructors_ex(
redis_destructor_redis_sock,
NULL,
redis_sock_name, module_number
);
add_constant_long(redis_ce, "REDIS_NOT_FOUND", REDIS_NOT_FOUND);
add_constant_long(redis_ce, "REDIS_STRING", REDIS_STRING);
add_constant_long(redis_ce, "REDIS_SET", REDIS_SET);
add_constant_long(redis_ce, "REDIS_LIST", REDIS_LIST);
add_constant_long(redis_ce, "REDIS_ZSET", REDIS_ZSET);
add_constant_long(redis_ce, "REDIS_HASH", REDIS_HASH);
add_constant_long(redis_ce, "ATOMIC", ATOMIC);
add_constant_long(redis_ce, "MULTI", MULTI);
add_constant_long(redis_ce, "PIPELINE", PIPELINE);
/* options */
add_constant_long(redis_ce, "OPT_SERIALIZER", REDIS_OPT_SERIALIZER);
add_constant_long(redis_ce, "OPT_PREFIX", REDIS_OPT_PREFIX);
add_constant_long(redis_ce, "OPT_READ_TIMEOUT", REDIS_OPT_READ_TIMEOUT);
/* serializer */
add_constant_long(redis_ce, "SERIALIZER_NONE", REDIS_SERIALIZER_NONE);
add_constant_long(redis_ce, "SERIALIZER_PHP", REDIS_SERIALIZER_PHP);
#ifdef HAVE_REDIS_IGBINARY
add_constant_long(redis_ce, "SERIALIZER_IGBINARY", REDIS_SERIALIZER_IGBINARY);
#endif
zend_declare_class_constant_stringl(redis_ce, "AFTER", 5, "after", 5 TSRMLS_CC);
zend_declare_class_constant_stringl(redis_ce, "BEFORE", 6, "before", 6 TSRMLS_CC);
#ifdef PHP_SESSION
/* declare session handler */
php_session_register_module(&ps_mod_redis);
#endif
return SUCCESS;
}
/**
* PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(redis)
{
return SUCCESS;
}
/**
* PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(redis)
{
return SUCCESS;
}
/**
* PHP_RSHUTDOWN_FUNCTION
*/
PHP_RSHUTDOWN_FUNCTION(redis)
{
return SUCCESS;
}
/**
* PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(redis)
{
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_end();
}
/* {{{ proto Redis Redis::__construct()
Public constructor */
PHP_METHOD(Redis, __construct)
{
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) {
RETURN_FALSE;
}
}
/* }}} */
/* {{{ proto Redis Redis::__destruct()
Public Destructor
*/
PHP_METHOD(Redis,__destruct) {
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) {
RETURN_FALSE;
}
// Grab our socket
RedisSock *redis_sock;
if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 1) < 0) {
RETURN_FALSE;
}
// If we think we're in MULTI mode, send a discard
if(redis_sock->mode == MULTI) {
// Discard any multi commands, and free any callbacks that have been queued
send_discard_static(redis_sock TSRMLS_CC);
free_reply_callbacks(getThis(), 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 {
/* reset multi/exec state if there is one. */
RedisSock *redis_sock;
if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
RETURN_TRUE;
}
}
/* }}} */
PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
zval *object;
zval **socket;
int host_len, id;
char *host = NULL;
long port = -1;
long retry_interval = 0;
char *persistent_id = NULL;
int persistent_id_len = -1;
double timeout = 0.0;
RedisSock *redis_sock = NULL;
#ifdef ZTS
/* not sure how in threaded mode this works so disabled persistents at first */
persistent = 0;
#endif
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ldsl",
&object, redis_ce, &host, &host_len, &port,
&timeout, &persistent_id, &persistent_id_len,
&retry_interval) == FAILURE) {
return FAILURE;
}
if (timeout < 0L || timeout > INT_MAX) {
zend_throw_exception(redis_exception_ce, "Invalid timeout", 0 TSRMLS_CC);
return FAILURE;
}
if (retry_interval < 0L || retry_interval > INT_MAX) {
zend_throw_exception(redis_exception_ce, "Invalid retry interval", 0 TSRMLS_CC);
return FAILURE;
}
if(port == -1 && host_len && host[0] != '/') { /* not unix socket, set to default value */
port = 6379;
}
/* if there is a redis sock already we have to remove it from the list */
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) > 0) {
if (zend_hash_find(Z_OBJPROP_P(object), "socket",
sizeof("socket"), (void **) &socket) == FAILURE)
{
/* maybe there is a socket but the id isn't known.. what to do? */
} else {
zend_list_delete(Z_LVAL_PP(socket)); /* the refcount should be decreased and the detructor called */
}
}
redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval, 0);
if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) {
redis_free_socket(redis_sock);
return FAILURE;
}
#if PHP_VERSION_ID >= 50400
id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC);
#else
id = zend_list_insert(redis_sock, le_redis_sock);
#endif
add_property_resource(object, "socket", id);
return SUCCESS;
}
/* {{{ proto boolean Redis::bitop(string op, string key, ...)
*/
PHP_METHOD(Redis, bitop)
{
char *cmd;
int cmd_len;
zval **z_args;
char **keys;
int *keys_len;
int argc = ZEND_NUM_ARGS(), i;
RedisSock *redis_sock = NULL;
smart_str buf = {0};
int key_free = 0;
/* get redis socket */
if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
/* fetch args */
z_args = emalloc(argc * sizeof(zval*));
if(zend_get_parameters_array(ht, argc, z_args) == FAILURE
|| argc < 3 /* 3 args min. */
|| Z_TYPE_P(z_args[0]) != IS_STRING /* operation must be a string. */
) {
efree(z_args);
RETURN_FALSE;
}
keys = emalloc(argc * sizeof(char*));
keys_len = emalloc(argc * sizeof(int));
/* prefix keys */
for(i = 0; i < argc; ++i) {
convert_to_string(z_args[i]);
keys[i] = Z_STRVAL_P(z_args[i]);
keys_len[i] = Z_STRLEN_P(z_args[i]);
if(i != 0)
key_free = redis_key_prefix(redis_sock, &keys[i], &keys_len[i] TSRMLS_CC);
}
/* start building the command */
smart_str_appendc(&buf, '*');
smart_str_append_long(&buf, argc + 1); /* +1 for BITOP command */
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
/* add command name */
smart_str_appendc(&buf, '$');
smart_str_append_long(&buf, 5);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
smart_str_appendl(&buf, "BITOP", 5);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
/* add keys */
for(i = 0; i < argc; ++i) {
smart_str_appendc(&buf, '$');
smart_str_append_long(&buf, keys_len[i]);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
smart_str_appendl(&buf, keys[i], keys_len[i]);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
}
/* end string */
smart_str_0(&buf);
cmd = buf.c;
cmd_len = buf.len;
/* cleanup */
if(key_free)
for(i = 1; i < argc; ++i) {
efree(keys[i]);
}
efree(keys);
efree(keys_len);
efree(z_args);
/* send */
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto boolean Redis::bitcount(string key, [int start], [int end])
*/
PHP_METHOD(Redis, bitcount)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
long start = 0, end = -1;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ll",
&object, redis_ce,
&key, &key_len, &start, &end) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
/* BITCOUNT key start end */
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "BITCOUNT", "sdd", key, key_len, (int)start, (int)end);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto boolean Redis::close()
*/
PHP_METHOD(Redis, close)
{
zval *object;
RedisSock *redis_sock = NULL;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
&object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
if (redis_sock_disconnect(redis_sock TSRMLS_CC)) {
RETURN_TRUE;
}
RETURN_FALSE;
}
/* }}} */
/* {{{ proto boolean Redis::set(string key, mixed value, long timeout | array options) */
PHP_METHOD(Redis, set) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *val = NULL, *cmd, *exp_type = NULL, *set_type = NULL;
int key_len, val_len, cmd_len;
long expire = -1;
int val_free = 0, key_free = 0;
zval *z_value, *z_opts = NULL;
// Make sure the arguments are correct
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|z",
&object, redis_ce, &key, &key_len, &z_value,
&z_opts) == FAILURE)
{
RETURN_FALSE;
}
// Ensure we can grab our redis socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
/* Our optional argument can either be a long (to support legacy SETEX */
/* redirection), or an array with Redis >= 2.6.12 set options */
if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY) {
RETURN_FALSE;
}
/* Serialization, key prefixing */
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
HashTable *kt = Z_ARRVAL_P(z_opts);
int type;
unsigned int ht_key_len;
unsigned long idx;
char *k;
zval **v;
/* Iterate our option array */
for(zend_hash_internal_pointer_reset(kt);
zend_hash_has_more_elements(kt) == SUCCESS;
zend_hash_move_forward(kt))
{
// Grab key and value
type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, NULL);
zend_hash_get_current_data(kt, (void**)&v);
if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) &&
(Z_LVAL_PP(v) > 0) && IS_EX_PX_ARG(k))
{
exp_type = k;
expire = Z_LVAL_PP(v);
} else if(Z_TYPE_PP(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_PP(v))) {
set_type = Z_STRVAL_PP(v);
}
}
} else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) {
expire = Z_LVAL_P(z_opts);
}
/* Now let's construct the command we want */
if(exp_type && set_type) {
/* SET <key> <value> NX|XX PX|EX <timeout> */
cmd_len = redis_cmd_format_static(&cmd, "SET", "ssssl", key, key_len,
val, val_len, set_type, 2, exp_type,
2, expire);
} else if(exp_type) {
/* SET <key> <value> PX|EX <timeout> */
cmd_len = redis_cmd_format_static(&cmd, "SET", "sssl", key, key_len,
val, val_len, exp_type, 2, expire);
} else if(set_type) {
/* SET <key> <value> NX|XX */
cmd_len = redis_cmd_format_static(&cmd, "SET", "sss", key, key_len,
val, val_len, set_type, 2);
} else if(expire > 0) {
/* Backward compatible SETEX redirection */
cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", key, key_len,
expire, val, val_len);
} else {
/* SET <key> <value> */
cmd_len = redis_cmd_format_static(&cmd, "SET", "ss", key, key_len,
val, val_len);
}
/* Free our key or value if we prefixed/serialized */
if(key_free) efree(key);
if(val_free) efree(val);
/* Kick off the command */
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
PHPAPI void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *val = NULL, *cmd;
int key_len, val_len, cmd_len;
long expire;
int val_free = 0, key_free = 0;
zval *z_value;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslz",
&object, redis_ce, &key, &key_len,
&expire, &z_value) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "sds", key, key_len, expire, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
/* {{{ proto boolean Redis::setex(string key, long expire, string value)
*/
PHP_METHOD(Redis, setex)
{
redis_generic_setex(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SETEX");
}
/* {{{ proto boolean Redis::psetex(string key, long expire, string value)
*/
PHP_METHOD(Redis, psetex)
{
redis_generic_setex(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PSETEX");
}
/* {{{ proto boolean Redis::setnx(string key, string value)
*/
PHP_METHOD(Redis, setnx)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *val = NULL, *cmd;
int key_len, val_len, cmd_len;
int val_free = 0, key_free = 0;
zval *z_value;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz",
&object, redis_ce, &key, &key_len,
&z_value) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_1_response);
}
/* }}} */
/* {{{ proto string Redis::getSet(string key, string value)
*/
PHP_METHOD(Redis, getSet)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *val = NULL, *cmd;
int key_len, val_len, cmd_len;
int val_free = 0, key_free = 0;
zval *z_value;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz",
&object, redis_ce, &key, &key_len,
&z_value) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_string_response);
}
/* }}} */
/* {{{ proto string Redis::randomKey()
*/
PHP_METHOD(Redis, randomKey)
{
zval *object;
RedisSock *redis_sock;
char *cmd;
int cmd_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
&object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
cmd_len = redis_cmd_format(&cmd, "*1" _NL "$9" _NL "RANDOMKEY" _NL);
/* TODO: remove prefix from key */
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_ping_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_ping_response);
}
/* }}} */
/* {{{ proto string Redis::echo(string key)
*/
PHP_METHOD(Redis, echo)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len;
int key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "ECHO", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_string_response);
}
/* }}} */
/* {{{ proto string Redis::renameKey(string key_src, string key_dst)
*/
PHP_METHOD(Redis, renameKey)
{
zval *object;
RedisSock *redis_sock;
char *cmd, *src, *dst;
int cmd_len, src_len, dst_len;
int src_free, dst_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
&object, redis_ce,
&src, &src_len,
&dst, &dst_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
src_free = redis_key_prefix(redis_sock, &src, &src_len TSRMLS_CC);
dst_free = redis_key_prefix(redis_sock, &dst, &dst_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "RENAME", "ss", src, src_len, dst, dst_len);
if(src_free) efree(src);
if(dst_free) efree(dst);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
/* }}} */
/* {{{ proto string Redis::renameNx(string key_src, string key_dst)
*/
PHP_METHOD(Redis, renameNx)
{
zval *object;
RedisSock *redis_sock;
char *cmd, *src, *dst;
int cmd_len, src_len, dst_len;
int src_free, dst_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
&object, redis_ce,
&src, &src_len,
&dst, &dst_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
src_free = redis_key_prefix(redis_sock, &src, &src_len TSRMLS_CC);
dst_free = redis_key_prefix(redis_sock, &dst, &dst_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "RENAMENX", "ss", src, src_len, dst, dst_len);
if(src_free) efree(src);
if(dst_free) efree(dst);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_1_response);
}
/* }}} */
/* {{{ proto string Redis::get(string key)
*/
PHP_METHOD(Redis, get)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len;
int key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "GET", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_string_response);
}
/* }}} */
/* {{{ proto string Redis::ping()
*/
PHP_METHOD(Redis, ping)
{
zval *object;
RedisSock *redis_sock;
char *cmd;
int cmd_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
&object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
cmd_len = redis_cmd_format_static(&cmd, "PING", "");
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_ping_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_ping_response);
}
/* }}} */
PHPAPI void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int count) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len;
long val = 1;
int key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l",
&object, redis_ce,
&key, &key_len, &val) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
if (val == 1) {
cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len);
} else {
cmd_len = redis_cmd_format_static(&cmd, keyword, "sl", key, key_len, val);
}
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* {{{ proto boolean Redis::incr(string key [,int value])
*/
PHP_METHOD(Redis, incr){
zval *object;
char *key = NULL;
int key_len;
long val = 1;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l",
&object, redis_ce,
&key, &key_len, &val) == FAILURE) {
RETURN_FALSE;
}
if(val == 1) {
redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INCR", 1);
} else {
redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INCRBY", val);
}
}
/* }}} */
/* {{{ proto boolean Redis::incrBy(string key ,int value)
*/
PHP_METHOD(Redis, incrBy){
zval *object;
char *key = NULL;
int key_len;
long val = 1;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl",
&object, redis_ce,
&key, &key_len, &val) == FAILURE) {
RETURN_FALSE;
}
if(val == 1) {
redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INCR", 1);
} else {
redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INCRBY", val);
}
}
/* }}} */
/* {{{ proto float Redis::incrByFloat(string key, float value)
*/
PHP_METHOD(Redis, incrByFloat) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
double val;
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osd",
&object, redis_ce, &key, &key_len, &val) == FAILURE) {
RETURN_FALSE;
}
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Prefix our key, free it if we have
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
if(key_free) efree(key);
// Format our INCRBYFLOAT command
cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_bulk_double_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_bulk_double_response);
}
/* {{{ proto boolean Redis::decr(string key [,int value])
*/
PHP_METHOD(Redis, decr)
{
zval *object;
char *key = NULL;
int key_len;
long val = 1;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l",
&object, redis_ce,
&key, &key_len, &val) == FAILURE) {
RETURN_FALSE;
}
if(val == 1) {
redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DECR", 1);
} else {
redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DECRBY", val);
}
}
/* }}} */
/* {{{ proto boolean Redis::decrBy(string key ,int value)
*/
PHP_METHOD(Redis, decrBy){
zval *object;
char *key = NULL;
int key_len;
long val = 1;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl",
&object, redis_ce,
&key, &key_len, &val) == FAILURE) {
RETURN_FALSE;
}
if(val == 1) {
redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DECR", 1);
} else {
redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DECRBY", val);
}
}
/* }}} */
/* {{{ proto array Redis::getMultiple(array keys)
*/
PHP_METHOD(Redis, getMultiple)
{
zval *object, *z_args, **z_ele;
HashTable *hash;
HashPosition ptr;
RedisSock *redis_sock;
smart_str cmd = {0};
int arg_count;
// Make sure we have proper arguments
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
&object, redis_ce, &z_args) == FAILURE) {
RETURN_FALSE;
}
// We'll need the socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Grab our array
hash = Z_ARRVAL_P(z_args);
// We don't need to do anything if there aren't any keys
if((arg_count = zend_hash_num_elements(hash)) == 0) {
RETURN_FALSE;
}
// Build our command header
redis_cmd_init_sstr(&cmd, arg_count, "MGET", 4);
// Iterate through and grab our keys
for(zend_hash_internal_pointer_reset_ex(hash, &ptr);
zend_hash_get_current_data_ex(hash, (void**)&z_ele, &ptr) == SUCCESS;
zend_hash_move_forward_ex(hash, &ptr))
{
char *key;
int key_len, key_free;
zval *z_tmp = NULL;
// If the key isn't a string, turn it into one
if(Z_TYPE_PP(z_ele) == IS_STRING) {
key = Z_STRVAL_PP(z_ele);
key_len = Z_STRLEN_PP(z_ele);
} else {
MAKE_STD_ZVAL(z_tmp);
*z_tmp = **z_ele;
zval_copy_ctor(z_tmp);
convert_to_string(z_tmp);
key = Z_STRVAL_P(z_tmp);
key_len = Z_STRLEN_P(z_tmp);
}
// Apply key prefix if necissary
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
// Append this key to our command
redis_cmd_append_sstr(&cmd, key, key_len);
// Free our key if it was prefixed
if(key_free) efree(key);
// Free oour temporary ZVAL if we converted from a non-string
if(z_tmp) {
zval_dtor(z_tmp);
efree(z_tmp);
z_tmp = NULL;
}
}
// Kick off our command
REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
IF_ATOMIC() {
if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
/* {{{ proto boolean Redis::exists(string key)
*/
PHP_METHOD(Redis, exists)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len;
int key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "EXISTS", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_1_response);
}
/* }}} */
/* {{{ proto boolean Redis::delete(string key)
*/
PHP_METHOD(Redis, delete)
{
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"DEL", sizeof("DEL") - 1,
1, &redis_sock, 0, 1, 1))
return;
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
PHPAPI void redis_set_watch(RedisSock *redis_sock)
{
redis_sock->watching = 1;
}
PHPAPI void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
{
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)
{
RedisSock *redis_sock;
generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"WATCH", sizeof("WATCH") - 1,
1, &redis_sock, 0, 1, 1);
redis_sock->watching = 1;
IF_ATOMIC() {
redis_watch_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_watch_response);
}
/* }}} */
PHPAPI void redis_clear_watch(RedisSock *redis_sock)
{
redis_sock->watching = 0;
}
PHPAPI void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
{
redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_clear_watch);
}
/* {{{ proto boolean Redis::unwatch()
*/
PHP_METHOD(Redis, unwatch)
{
char cmd[] = "*1" _NL "$7" _NL "UNWATCH" _NL;
generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, estrdup(cmd), sizeof(cmd)-1, redis_unwatch_response);
}
/* }}} */
/* {{{ proto array Redis::getKeys(string pattern)
*/
PHP_METHOD(Redis, getKeys)
{
zval *object;
RedisSock *redis_sock;
char *pattern = NULL, *cmd;
int pattern_len, cmd_len, pattern_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&pattern, &pattern_len) == FAILURE) {
RETURN_NULL();
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s", pattern, pattern_len);
if(pattern_free) efree(pattern);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw);
}
/* }}} */
/* {{{ proto int Redis::type(string key)
*/
PHP_METHOD(Redis, type)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_NULL();
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "TYPE", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_type_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_type_response);
}
/* }}} */
PHP_METHOD(Redis, append)
{
zval *object;
RedisSock *redis_sock;
char *cmd;
int cmd_len, key_len, val_len, key_free;
char *key, *val;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
&object, redis_ce,
&key, &key_len, &val, &val_len) == FAILURE) {
RETURN_NULL();
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "APPEND", "ss", key, key_len, val, val_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
PHP_METHOD(Redis, getRange)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
long start, end;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll",
&object, redis_ce, &key, &key_len,
&start, &end) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "GETRANGE", "sdd", key, key_len, (int)start, (int)end);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_string_response);
}
PHP_METHOD(Redis, setRange)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *val, *cmd;
int key_len, val_len, cmd_len, key_free;
long offset;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osls",
&object, redis_ce, &key, &key_len,
&offset, &val, &val_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SETRANGE", "sds", key, key_len, (int)offset, val, val_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
PHP_METHOD(Redis, getBit)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
long offset;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl",
&object, redis_ce, &key, &key_len,
&offset) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "GETBIT", "sd", key, key_len, (int)offset);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
PHP_METHOD(Redis, setBit)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
long offset;
zend_bool val;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslb",
&object, redis_ce, &key, &key_len,
&offset, &val) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SETBIT", "sdd", key, key_len, (int)offset, (int)val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
PHP_METHOD(Redis, strlen)
{
zval *object;
RedisSock *redis_sock;
char *cmd;
int cmd_len, key_len, key_free;
char *key;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_NULL();
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "STRLEN", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
PHPAPI void
generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
zval *object;
RedisSock *redis_sock;
char *cmd, *key, *val;
int cmd_len, key_len, val_len;
zval *z_value;
int val_free, key_free = 0;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz",
&object, redis_ce,
&key, &key_len, &z_value) == FAILURE) {
RETURN_NULL();
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* {{{ proto boolean Redis::lPush(string key , string value)
*/
PHP_METHOD(Redis, lPush)
{
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"LPUSH", sizeof("LPUSH") - 1,
2, &redis_sock, 0, 0, 1))
return;
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto boolean Redis::rPush(string key , string value)
*/
PHP_METHOD(Redis, rPush)
{
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"RPUSH", sizeof("RPUSH") - 1,
2, &redis_sock, 0, 0, 1))
return;
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
PHP_METHOD(Redis, lInsert)
{
zval *object;
RedisSock *redis_sock;
char *pivot, *position, *key, *val, *cmd;
int pivot_len, position_len, key_len, val_len, cmd_len;
int val_free, pivot_free, key_free;
zval *z_value, *z_pivot;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osszz",
&object, redis_ce,
&key, &key_len,
&position, &position_len,
&z_pivot,
&z_value) == FAILURE) {
RETURN_NULL();
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
if(strncasecmp(position, "after", 5) == 0 || strncasecmp(position, "before", 6) == 0) {
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss", key, key_len, position, position_len, pivot, pivot_len, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
if(pivot_free) efree(pivot);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
} else {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error on position");
}
}
PHP_METHOD(Redis, lPushx)
{
generic_push_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "LPUSHX", sizeof("LPUSHX")-1);
}
PHP_METHOD(Redis, rPushx)
{
generic_push_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "RPUSHX", sizeof("RPUSHX")-1);
}
PHPAPI void
generic_pop_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_string_response);
}
/* {{{ proto string Redis::lPOP(string key)
*/
PHP_METHOD(Redis, lPop)
{
generic_pop_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "LPOP", sizeof("LPOP")-1);
}
/* }}} */
/* {{{ proto string Redis::rPOP(string key)
*/
PHP_METHOD(Redis, rPop)
{
generic_pop_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "RPOP", sizeof("RPOP")-1);
}
/* }}} */
/* {{{ proto string Redis::blPop(string key1, string key2, ..., int timeout)
*/
PHP_METHOD(Redis, blPop)
{
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"BLPOP", sizeof("BLPOP") - 1,
2, &redis_sock, 1, 1, 1))
return;
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
/* }}} */
/* {{{ proto string Redis::brPop(string key1, string key2, ..., int timeout)
*/
PHP_METHOD(Redis, brPop)
{
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"BRPOP", sizeof("BRPOP") - 1,
2, &redis_sock, 1, 1, 1))
return;
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
/* }}} */
/* {{{ proto int Redis::lSize(string key)
*/
PHP_METHOD(Redis, lSize)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "LLEN", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto boolean Redis::lRemove(string list, string value, int count = 0)
*/
PHP_METHOD(Redis, lRemove)
{
zval *object;
RedisSock *redis_sock;
char *cmd;
int cmd_len, key_len, val_len;
char *key, *val;
long count = 0;
zval *z_value;
int val_free, key_free = 0;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|l",
&object, redis_ce,
&key, &key_len, &z_value, &count) == FAILURE) {
RETURN_NULL();
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
/* LREM key count value */
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds", key, key_len, count, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto boolean Redis::listTrim(string key , int start , int end)
*/
PHP_METHOD(Redis, listTrim)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
long start, end;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll",
&object, redis_ce, &key, &key_len,
&start, &end) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "LTRIM", "sdd", key, key_len, (int)start, (int)end);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
/* }}} */
/* {{{ proto string Redis::lGet(string key , int index)
*/
PHP_METHOD(Redis, lGet)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
long index;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl",
&object, redis_ce,
&key, &key_len, &index) == FAILURE) {
RETURN_NULL();
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
/* LINDEX key pos */
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "LINDEX", "sd", key, key_len, (int)index);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_string_response);
}
/* }}} */
/* {{{ proto array Redis::lGetRange(string key, int start , int end)
*/
PHP_METHOD(Redis, lGetRange)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
long start, end;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll",
&object, redis_ce,
&key, &key_len, &start, &end) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
/* LRANGE key start end */
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "LRANGE", "sdd", key, key_len, (int)start, (int)end);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
/* }}} */
/* {{{ proto boolean Redis::sAdd(string key , mixed value)
*/
PHP_METHOD(Redis, sAdd)
{
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"SADD", sizeof("SADD") - 1,
2, &redis_sock, 0, 0, 1))
return;
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto int Redis::sSize(string key)
*/
PHP_METHOD(Redis, sSize)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SCARD", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto boolean Redis::sRemove(string set, string value)
*/
PHP_METHOD(Redis, sRemove)
{
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"SREM", sizeof("SREM") - 1,
2, &redis_sock, 0, 0, 1))
return;
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto boolean Redis::sMove(string set_src, string set_dst, mixed value)
*/
PHP_METHOD(Redis, sMove)
{
zval *object;
RedisSock *redis_sock;
char *src = NULL, *dst = NULL, *val = NULL, *cmd;
int src_len, dst_len, val_len, cmd_len;
int val_free, src_free, dst_free;
zval *z_value;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ossz",
&object, redis_ce,
&src, &src_len,
&dst, &dst_len,
&z_value) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
src_free = redis_key_prefix(redis_sock, &src, &src_len TSRMLS_CC);
dst_free = redis_key_prefix(redis_sock, &dst, &dst_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss", src, src_len, dst, dst_len, val, val_len);
if(val_free) efree(val);
if(src_free) efree(src);
if(dst_free) efree(dst);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_1_response);
}
/* }}} */
/* }}} */
/* {{{ proto string Redis::sPop(string key)
*/
PHP_METHOD(Redis, sPop)
{
generic_pop_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SPOP", 4);
}
/* }}} */
/* }}} */
/* {{{ proto string Redis::sRandMember(string key [int count])
*/
PHP_METHOD(Redis, sRandMember)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free = 0;
long count;
// Parse our params
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l",
&object, redis_ce, &key, &key_len, &count) == FAILURE) {
RETURN_FALSE;
}
// Get our redis socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Prefix our key if necissary
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
// If we have two arguments, we're running with an optional COUNT, which will return
// a multibulk reply. Without the argument we'll return a string response
if(ZEND_NUM_ARGS() == 2) {
// Construct our command with count
cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "sl", key, key_len, count);
} else {
// Construct our command
cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "s", key, key_len);
}
// Free our key if we prefixed it
if(key_free) efree(key);
// Process our command
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
// Process our reply
IF_ATOMIC() {
// This will be bulk or multi-bulk depending if we passed the optional [COUNT] argument
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
}
/* }}} */
/* {{{ proto boolean Redis::sContains(string set, string value)
*/
PHP_METHOD(Redis, sContains)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *val = NULL, *cmd;
int key_len, val_len, cmd_len;
int val_free, key_free = 0;
zval *z_value;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz",
&object, redis_ce,
&key, &key_len, &z_value) == FAILURE) {
return;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_1_response);
}
/* }}} */
/* {{{ proto array Redis::sMembers(string set)
*/
PHP_METHOD(Redis, sMembers)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SMEMBERS", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
/* }}} */
PHPAPI int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len,
int min_argc, RedisSock **out_sock, int has_timeout, int all_keys, int can_serialize)
{
zval **z_args, *z_array;
char **keys, *cmd;
int cmd_len, *keys_len, *keys_to_free;
int i, j, argc = ZEND_NUM_ARGS(), real_argc = 0;
int single_array = 0;
int timeout = 0;
int pos;
int array_size;
RedisSock *redis_sock;
if(argc < min_argc) {
zend_wrong_param_count(TSRMLS_C);
ZVAL_BOOL(return_value, 0);
return FAILURE;
}
/* get redis socket */
if (redis_sock_get(getThis(), out_sock TSRMLS_CC, 0) < 0) {
ZVAL_BOOL(return_value, 0);
return FAILURE;
}
redis_sock = *out_sock;
z_args = emalloc(argc * sizeof(zval*));
if(zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
efree(z_args);
ZVAL_BOOL(return_value, 0);
return FAILURE;
}
/* case of a single array */
if(has_timeout == 0) {
if(argc == 1 && Z_TYPE_P(z_args[0]) == IS_ARRAY) {
single_array = 1;
z_array = z_args[0];
efree(z_args);
z_args = NULL;
/* new count */
argc = zend_hash_num_elements(Z_ARRVAL_P(z_array));
}
} else if(has_timeout == 1) {
if(argc == 2 && Z_TYPE_P(z_args[0]) == IS_ARRAY && Z_TYPE_P(z_args[1]) == IS_LONG) {
single_array = 1;
z_array = z_args[0];
timeout = Z_LVAL_P(z_args[1]);
efree(z_args);
z_args = NULL;
/* new count */
argc = zend_hash_num_elements(Z_ARRVAL_P(z_array));
}
}
/* prepare an array for the keys, one for their lengths, one to mark the keys to free. */
array_size = argc;
if(has_timeout)
array_size++;
keys = emalloc(array_size * sizeof(char*));
keys_len = emalloc(array_size * sizeof(int));
keys_to_free = emalloc(array_size * sizeof(int));
memset(keys_to_free, 0, array_size * sizeof(int));
cmd_len = 1 + integer_length(keyword_len) + 2 +keyword_len + 2; /* start computing the command length */
if(single_array) { /* loop over the array */
HashTable *keytable = Z_ARRVAL_P(z_array);
for(j = 0, zend_hash_internal_pointer_reset(keytable);
zend_hash_has_more_elements(keytable) == SUCCESS;
zend_hash_move_forward(keytable)) {
char *key;
unsigned int key_len;
unsigned long idx;
zval **z_value_pp;
zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, NULL);
if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) {
continue; /* this should never happen, according to the PHP people. */
}
if(!all_keys && j != 0) { /* not just operating on keys */
if(can_serialize) {
keys_to_free[j] = redis_serialize(redis_sock, *z_value_pp, &keys[j], &keys_len[j] TSRMLS_CC);
} else {
convert_to_string(*z_value_pp);
keys[j] = Z_STRVAL_PP(z_value_pp);
keys_len[j] = Z_STRLEN_PP(z_value_pp);
keys_to_free[j] = 0;
}
} else {
/* only accept strings */
if(Z_TYPE_PP(z_value_pp) != IS_STRING) {
convert_to_string(*z_value_pp);
}
/* get current value */
keys[j] = Z_STRVAL_PP(z_value_pp);
keys_len[j] = Z_STRLEN_PP(z_value_pp);
keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], &keys_len[j] TSRMLS_CC); /* add optional prefix */
}
cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; /* $ + size + NL + string + NL */
j++;
real_argc++;
}
if(has_timeout) {
keys_len[j] = spprintf(&keys[j], 0, "%d", timeout);
cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; // $ + size + NL + string + NL
j++;
real_argc++;
}
} else {
if(has_timeout && Z_TYPE_P(z_args[argc - 1]) != IS_LONG) {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Syntax error on timeout");
}
for(i = 0, j = 0; i < argc; ++i) { /* store each key */
if(!all_keys && j != 0) { /* not just operating on keys */
if(can_serialize) {
keys_to_free[j] = redis_serialize(redis_sock, z_args[i], &keys[j], &keys_len[j] TSRMLS_CC);
} else {
convert_to_string(z_args[i]);
keys[j] = Z_STRVAL_P(z_args[i]);
keys_len[j] = Z_STRLEN_P(z_args[i]);
keys_to_free[j] = 0;
}
} else {
if(Z_TYPE_P(z_args[i]) != IS_STRING) {
convert_to_string(z_args[i]);
}
keys[j] = Z_STRVAL_P(z_args[i]);
keys_len[j] = Z_STRLEN_P(z_args[i]);
// If we have a timeout it should be the last argument, which we do not want to prefix
if(!has_timeout || i < argc-1) {
keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], &keys_len[j] TSRMLS_CC); /* add optional prefix TSRMLS_CC*/
}
}
cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; /* $ + size + NL + string + NL */
j++;
real_argc++;
}
}
cmd_len += 1 + integer_length(real_argc+1) + 2; // *count NL
cmd = emalloc(cmd_len+1);
sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1+real_argc, keyword_len, keyword);
pos = 1 +integer_length(real_argc + 1) + 2
+ 1 + integer_length(keyword_len) + 2
+ keyword_len + 2;
/* copy each key to its destination */
for(i = 0; i < real_argc; ++i) {
sprintf(cmd + pos, "$%d" _NL, keys_len[i]); // size
pos += 1 + integer_length(keys_len[i]) + 2;
memcpy(cmd + pos, keys[i], keys_len[i]);
pos += keys_len[i];
memcpy(cmd + pos, _NL, 2);
pos += 2;
}
/* cleanup prefixed keys. */
for(i = 0; i < real_argc + (has_timeout?-1:0); ++i) {
if(keys_to_free[i])
efree(keys[i]);
}
if(single_array && has_timeout) { /* cleanup string created to contain timeout value */
efree(keys[real_argc-1]);
}
efree(keys);
efree(keys_len);
efree(keys_to_free);
if(z_args) efree(z_args);
/* call REDIS_PROCESS_REQUEST and skip void returns */
IF_MULTI_OR_ATOMIC() {
if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
efree(cmd);
return FAILURE;
}
efree(cmd);
}
IF_PIPELINE() {
PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len);
efree(cmd);
}
return SUCCESS;
}
/* {{{ proto array Redis::sInter(string key0, ... string keyN)
*/
PHP_METHOD(Redis, sInter) {
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"SINTER", sizeof("SINTER") - 1,
0, &redis_sock, 0, 1, 1))
return;
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
/* }}} */
/* {{{ proto array Redis::sInterStore(string destination, string key0, ... string keyN)
*/
PHP_METHOD(Redis, sInterStore) {
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"SINTERSTORE", sizeof("SINTERSTORE") - 1,
1, &redis_sock, 0, 1, 1))
return;
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto array Redis::sUnion(string key0, ... string keyN)
*/
PHP_METHOD(Redis, sUnion) {
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"SUNION", sizeof("SUNION") - 1,
0, &redis_sock, 0, 1, 1))
return;
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
/* }}} */
/* {{{ proto array Redis::sUnionStore(string destination, string key0, ... string keyN)
*/
PHP_METHOD(Redis, sUnionStore) {
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"SUNIONSTORE", sizeof("SUNIONSTORE") - 1,
1, &redis_sock, 0, 1, 1))
return;
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto array Redis::sDiff(string key0, ... string keyN)
*/
PHP_METHOD(Redis, sDiff) {
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"SDIFF", sizeof("SDIFF") - 1,
0, &redis_sock, 0, 1, 1))
return;
IF_ATOMIC() {
/* read multibulk reply */
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
/* }}} */
/* {{{ proto array Redis::sDiffStore(string destination, string key0, ... string keyN)
*/
PHP_METHOD(Redis, sDiffStore) {
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"SDIFFSTORE", sizeof("SDIFFSTORE") - 1,
1, &redis_sock, 0, 1, 1))
return;
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
PHP_METHOD(Redis, sort) {
zval *object = getThis(), *z_array = NULL, **z_cur;
char *cmd, *old_cmd = NULL, *key;
int cmd_len, elements = 2, key_len, key_free;
int using_store = 0;
RedisSock *redis_sock;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|a",
&object, redis_ce,
&key, &key_len, &z_array) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format(&cmd, "$4" _NL "SORT" _NL "$%d" _NL "%s" _NL, key_len, key, key_len);
if(key_free) efree(key);
if(z_array) {
if ((zend_hash_find(Z_ARRVAL_P(z_array), "by", sizeof("by"), (void **) &z_cur) == SUCCESS
|| zend_hash_find(Z_ARRVAL_P(z_array), "BY", sizeof("BY"), (void **) &z_cur) == SUCCESS)
&& Z_TYPE_PP(z_cur) == IS_STRING) {
old_cmd = cmd;
cmd_len = redis_cmd_format(&cmd, "%s"
"$2" _NL
"BY" _NL
"$%d" _NL
"%s" _NL
, cmd, cmd_len
, Z_STRLEN_PP(z_cur), Z_STRVAL_PP(z_cur), Z_STRLEN_PP(z_cur));
elements += 2;
efree(old_cmd);
}
if ((zend_hash_find(Z_ARRVAL_P(z_array), "sort", sizeof("sort"), (void **) &z_cur) == SUCCESS
|| zend_hash_find(Z_ARRVAL_P(z_array), "SORT", sizeof("SORT"), (void **) &z_cur) == SUCCESS)
&& Z_TYPE_PP(z_cur) == IS_STRING) {
old_cmd = cmd;
cmd_len = redis_cmd_format(&cmd, "%s"
"$%d" _NL
"%s" _NL
, cmd, cmd_len
, Z_STRLEN_PP(z_cur), Z_STRVAL_PP(z_cur), Z_STRLEN_PP(z_cur));
elements += 1;
efree(old_cmd);
}
if ((zend_hash_find(Z_ARRVAL_P(z_array), "store", sizeof("store"), (void **) &z_cur) == SUCCESS
|| zend_hash_find(Z_ARRVAL_P(z_array), "STORE", sizeof("STORE"), (void **) &z_cur) == SUCCESS)
&& Z_TYPE_PP(z_cur) == IS_STRING) {
using_store = 1;
old_cmd = cmd;
cmd_len = redis_cmd_format(&cmd, "%s"
"$5" _NL
"STORE" _NL
"$%d" _NL
"%s" _NL
, cmd, cmd_len
, Z_STRLEN_PP(z_cur), Z_STRVAL_PP(z_cur), Z_STRLEN_PP(z_cur));
elements += 2;
efree(old_cmd);
}
if ((zend_hash_find(Z_ARRVAL_P(z_array), "get", sizeof("get"), (void **) &z_cur) == SUCCESS
|| zend_hash_find(Z_ARRVAL_P(z_array), "GET", sizeof("GET"), (void **) &z_cur) == SUCCESS)
&& (Z_TYPE_PP(z_cur) == IS_STRING || Z_TYPE_PP(z_cur) == IS_ARRAY)) {
if(Z_TYPE_PP(z_cur) == IS_STRING) {
old_cmd = cmd;
cmd_len = redis_cmd_format(&cmd, "%s"
"$3" _NL
"GET" _NL
"$%d" _NL
"%s" _NL
, cmd, cmd_len
, Z_STRLEN_PP(z_cur), Z_STRVAL_PP(z_cur), Z_STRLEN_PP(z_cur));
elements += 2;
efree(old_cmd);
} else if(Z_TYPE_PP(z_cur) == IS_ARRAY) { // loop over the strings in that array and add them as patterns
HashTable *keytable = Z_ARRVAL_PP(z_cur);
for(zend_hash_internal_pointer_reset(keytable);
zend_hash_has_more_elements(keytable) == SUCCESS;
zend_hash_move_forward(keytable)) {
char *key;
unsigned int key_len;
unsigned long idx;
zval **z_value_pp;
zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, NULL);
if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) {
continue; /* this should never happen, according to the PHP people. */
}
if(Z_TYPE_PP(z_value_pp) == IS_STRING) {
old_cmd = cmd;
cmd_len = redis_cmd_format(&cmd, "%s"
"$3" _NL
"GET" _NL
"$%d" _NL
"%s" _NL
, cmd, cmd_len
, Z_STRLEN_PP(z_value_pp), Z_STRVAL_PP(z_value_pp), Z_STRLEN_PP(z_value_pp));
elements += 2;
efree(old_cmd);
}
}
}
}
if ((zend_hash_find(Z_ARRVAL_P(z_array), "alpha", sizeof("alpha"), (void **) &z_cur) == SUCCESS
|| zend_hash_find(Z_ARRVAL_P(z_array), "ALPHA", sizeof("ALPHA"), (void **) &z_cur) == SUCCESS)
&& Z_TYPE_PP(z_cur) == IS_BOOL && Z_BVAL_PP(z_cur) == 1) {
old_cmd = cmd;
cmd_len = redis_cmd_format(&cmd, "%s"
"$5" _NL
"ALPHA" _NL
, cmd, cmd_len);
elements += 1;
efree(old_cmd);
}
if ((zend_hash_find(Z_ARRVAL_P(z_array), "limit", sizeof("limit"), (void **) &z_cur) == SUCCESS
|| zend_hash_find(Z_ARRVAL_P(z_array), "LIMIT", sizeof("LIMIT"), (void **) &z_cur) == SUCCESS)
&& Z_TYPE_PP(z_cur) == IS_ARRAY) {
if(zend_hash_num_elements(Z_ARRVAL_PP(z_cur)) == 2) {
zval **z_offset_pp, **z_count_pp;
// get the two values from the table, check that they are indeed of LONG type
if(SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_cur), 0, (void**)&z_offset_pp) &&
SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_cur), 1, (void**)&z_count_pp)) {
long limit_low, limit_high;
if((Z_TYPE_PP(z_offset_pp) == IS_LONG || Z_TYPE_PP(z_offset_pp) == IS_STRING) &&
(Z_TYPE_PP(z_count_pp) == IS_LONG || Z_TYPE_PP(z_count_pp) == IS_STRING)) {
if(Z_TYPE_PP(z_offset_pp) == IS_LONG) {
limit_low = Z_LVAL_PP(z_offset_pp);
} else {
limit_low = atol(Z_STRVAL_PP(z_offset_pp));
}
if(Z_TYPE_PP(z_count_pp) == IS_LONG) {
limit_high = Z_LVAL_PP(z_count_pp);
} else {
limit_high = atol(Z_STRVAL_PP(z_count_pp));
}
old_cmd = cmd;
cmd_len = redis_cmd_format(&cmd, "%s"
"$5" _NL
"LIMIT" _NL
"$%d" _NL
"%d" _NL
"$%d" _NL
"%d" _NL
, cmd, cmd_len
, integer_length(limit_low), limit_low
, integer_length(limit_high), limit_high);
elements += 3;
efree(old_cmd);
}
}
}
}
}
/* complete with prefix */
old_cmd = cmd;
cmd_len = redis_cmd_format(&cmd, "*%d" _NL "%s", elements, cmd, cmd_len);
efree(old_cmd);
/* run command */
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
if(using_store) {
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
} else {
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
}
PHPAPI void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int use_alpha) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *pattern = NULL, *get = NULL, *store = NULL, *cmd;
int key_len, pattern_len = -1, get_len = -1, store_len = -1, cmd_len, key_free;
long sort_start = -1, sort_count = -1;
int cmd_elements;
char *cmd_lines[30];
int cmd_sizes[30];
int sort_len;
int i, pos;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|sslls",
&object, redis_ce,
&key, &key_len, &pattern, &pattern_len,
&get, &get_len, &sort_start, &sort_count, &store, &store_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
if(key_len == 0) {
RETURN_FALSE;
}
/* first line, sort. */
cmd_lines[1] = estrdup("$4");
cmd_sizes[1] = 2;
cmd_lines[2] = estrdup("SORT");
cmd_sizes[2] = 4;
// Prefix our key if we need to
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
/* second line, key */
cmd_sizes[3] = redis_cmd_format(&cmd_lines[3], "$%d", key_len);
cmd_lines[4] = emalloc(key_len + 1);
memcpy(cmd_lines[4], key, key_len);
cmd_lines[4][key_len] = 0;
cmd_sizes[4] = key_len;
// If we prefixed our key, free it
if(key_free) efree(key);
cmd_elements = 5;
if(pattern && pattern_len) {
/* BY */
cmd_lines[cmd_elements] = estrdup("$2");
cmd_sizes[cmd_elements] = 2;
cmd_elements++;
cmd_lines[cmd_elements] = estrdup("BY");
cmd_sizes[cmd_elements] = 2;
cmd_elements++;
/* pattern */
cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", pattern_len);
cmd_elements++;
cmd_lines[cmd_elements] = emalloc(pattern_len + 1);
memcpy(cmd_lines[cmd_elements], pattern, pattern_len);
cmd_lines[cmd_elements][pattern_len] = 0;
cmd_sizes[cmd_elements] = pattern_len;
cmd_elements++;
}
if(sort_start >= 0 && sort_count >= 0) {
/* LIMIT */
cmd_lines[cmd_elements] = estrdup("$5");
cmd_sizes[cmd_elements] = 2;
cmd_elements++;
cmd_lines[cmd_elements] = estrdup("LIMIT");
cmd_sizes[cmd_elements] = 5;
cmd_elements++;
/* start */
cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", integer_length(sort_start));
cmd_elements++;
cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, "%d", (int)sort_start);
cmd_elements++;
/* count */
cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", integer_length(sort_count));
cmd_elements++;
cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, "%d", (int)sort_count);
cmd_elements++;
}
if(get && get_len) {
/* GET */
cmd_lines[cmd_elements] = estrdup("$3");
cmd_sizes[cmd_elements] = 2;
cmd_elements++;
cmd_lines[cmd_elements] = estrdup("GET");
cmd_sizes[cmd_elements] = 3;
cmd_elements++;
/* pattern */
cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", get_len);
cmd_elements++;
cmd_lines[cmd_elements] = emalloc(get_len + 1);
memcpy(cmd_lines[cmd_elements], get, get_len);
cmd_lines[cmd_elements][get_len] = 0;
cmd_sizes[cmd_elements] = get_len;
cmd_elements++;
}
/* add ASC or DESC */
sort_len = strlen(sort);
cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", sort_len);
cmd_elements++;
cmd_lines[cmd_elements] = emalloc(sort_len + 1);
memcpy(cmd_lines[cmd_elements], sort, sort_len);
cmd_lines[cmd_elements][sort_len] = 0;
cmd_sizes[cmd_elements] = sort_len;
cmd_elements++;
if(use_alpha) {
/* ALPHA */
cmd_lines[cmd_elements] = estrdup("$5");
cmd_sizes[cmd_elements] = 2;
cmd_elements++;
cmd_lines[cmd_elements] = estrdup("ALPHA");
cmd_sizes[cmd_elements] = 5;
cmd_elements++;
}
if(store && store_len) {
/* STORE */
cmd_lines[cmd_elements] = estrdup("$5");
cmd_sizes[cmd_elements] = 2;
cmd_elements++;
cmd_lines[cmd_elements] = estrdup("STORE");
cmd_sizes[cmd_elements] = 5;
cmd_elements++;
/* store key */
cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", store_len);
cmd_elements++;
cmd_lines[cmd_elements] = emalloc(store_len + 1);
memcpy(cmd_lines[cmd_elements], store, store_len);
cmd_lines[cmd_elements][store_len] = 0;
cmd_sizes[cmd_elements] = store_len;
cmd_elements++;
}
/* first line has the star */
cmd_sizes[0] = spprintf(&cmd_lines[0], 0, "*%d", (cmd_elements-1)/2);
/* compute the command size */
cmd_len = 0;
for(i = 0; i < cmd_elements; ++i) {
cmd_len += cmd_sizes[i] + sizeof(_NL) - 1; /* each line followeb by _NL */
}
/* copy all lines into the final command. */
cmd = emalloc(1 + cmd_len);
pos = 0;
for(i = 0; i < cmd_elements; ++i) {
memcpy(cmd + pos, cmd_lines[i], cmd_sizes[i]);
pos += cmd_sizes[i];
memcpy(cmd + pos, _NL, sizeof(_NL) - 1);
pos += sizeof(_NL) - 1;
/* free every line */
efree(cmd_lines[i]);
}
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
/* {{{ proto array Redis::sortAsc(string key, string pattern, string get, int start, int end, bool getList])
*/
PHP_METHOD(Redis, sortAsc)
{
generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ASC", 0);
}
/* }}} */
/* {{{ proto array Redis::sortAscAlpha(string key, string pattern, string get, int start, int end, bool getList])
*/
PHP_METHOD(Redis, sortAscAlpha)
{
generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ASC", 1);
}
/* }}} */
/* {{{ proto array Redis::sortDesc(string key, string pattern, string get, int start, int end, bool getList])
*/
PHP_METHOD(Redis, sortDesc)
{
generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DESC", 0);
}
/* }}} */
/* {{{ proto array Redis::sortDescAlpha(string key, string pattern, string get, int start, int end, bool getList])
*/
PHP_METHOD(Redis, sortDescAlpha)
{
generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DESC", 1);
}
/* }}} */
PHPAPI void generic_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd, *t;
int key_len, cmd_len, key_free, t_len;
int i;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
&object, redis_ce, &key, &key_len,
&t, &t_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
/* check that we have a number */
for(i = 0; i < t_len; ++i)
if(t[i] < '0' || t[i] > '9')
RETURN_FALSE;
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, t, t_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_1_response);
}
/* {{{ proto array Redis::setTimeout(string key, int timeout)
*/
PHP_METHOD(Redis, setTimeout) {
generic_expire_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "EXPIRE", sizeof("EXPIRE")-1);
}
PHP_METHOD(Redis, pexpire) {
generic_expire_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PEXPIRE", sizeof("PEXPIRE")-1);
}
/* }}} */
/* {{{ proto array Redis::expireAt(string key, int timestamp)
*/
PHP_METHOD(Redis, expireAt) {
generic_expire_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "EXPIREAT", sizeof("EXPIREAT")-1);
}
/* }}} */
/* {{{ proto array Redis::pexpireAt(string key, int timestamp)
*/
PHP_METHOD(Redis, pexpireAt) {
generic_expire_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PEXPIREAT", sizeof("PEXPIREAT")-1);
}
/* }}} */
/* {{{ proto array Redis::lSet(string key, int index, string value)
*/
PHP_METHOD(Redis, lSet) {
zval *object;
RedisSock *redis_sock;
char *cmd;
int cmd_len, key_len, val_len;
long index;
char *key, *val;
int val_free, key_free = 0;
zval *z_value;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslz",
&object, redis_ce, &key, &key_len, &index, &z_value) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds", key, key_len, index, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
/* }}} */
PHPAPI void generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ResultCallback result_callback) {
zval *object;
RedisSock *redis_sock;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
&object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
result_callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(result_callback);
}
PHPAPI void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...) {
generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len, redis_boolean_response);
}
/* {{{ proto string Redis::save()
*/
PHP_METHOD(Redis, save)
{
char *cmd;
int cmd_len = redis_cmd_format_static(&cmd, "SAVE", "");
generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len);
}
/* }}} */
/* {{{ proto string Redis::bgSave()
*/
PHP_METHOD(Redis, bgSave)
{
char *cmd;
int cmd_len = redis_cmd_format_static(&cmd, "BGSAVE", "");
generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len);
}
/* }}} */
PHPAPI void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...) {
zval *object;
RedisSock *redis_sock;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
&object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* {{{ proto integer Redis::lastSave()
*/
PHP_METHOD(Redis, lastSave)
{
char *cmd;
int cmd_len = redis_cmd_format_static(&cmd, "LASTSAVE", "");
generic_empty_long_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len);
}
/* }}} */
/* {{{ proto bool Redis::flushDB()
*/
PHP_METHOD(Redis, flushDB)
{
char *cmd;
int cmd_len = redis_cmd_format_static(&cmd, "FLUSHDB", "");
generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len);
}
/* }}} */
/* {{{ proto bool Redis::flushAll()
*/
PHP_METHOD(Redis, flushAll)
{
char *cmd;
int cmd_len = redis_cmd_format_static(&cmd, "FLUSHALL", "");
generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len);
}
/* }}} */
/* {{{ proto int Redis::dbSize()
*/
PHP_METHOD(Redis, dbSize)
{
char *cmd;
int cmd_len = redis_cmd_format_static(&cmd, "DBSIZE", "");
generic_empty_long_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len);
}
/* }}} */
/* {{{ proto bool Redis::auth(string passwd)
*/
PHP_METHOD(Redis, auth) {
zval *object;
RedisSock *redis_sock;
char *cmd, *password;
int cmd_len, password_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce, &password, &password_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", password, password_len);
// Free previously stored auth if we have one, and store this password
if(redis_sock->auth) efree(redis_sock->auth);
redis_sock->auth = estrndup(password, password_len);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
/* }}} */
/* {{{ proto long Redis::persist(string key)
*/
PHP_METHOD(Redis, persist) {
zval *object;
RedisSock *redis_sock;
char *cmd, *key;
int cmd_len, key_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce, &key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "PERSIST", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_1_response);
}
/* }}} */
PHPAPI void generic_ttl(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
zval *object;
RedisSock *redis_sock;
char *cmd, *key;
int cmd_len, key_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce, &key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* {{{ proto long Redis::ttl(string key)
*/
PHP_METHOD(Redis, ttl) {
generic_ttl(INTERNAL_FUNCTION_PARAM_PASSTHRU, "TTL");
}
/* }}} */
/* {{{ proto long Redis::pttl(string key)
*/
PHP_METHOD(Redis, pttl) {
generic_ttl(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PTTL");
}
/* }}} */
/* {{{ proto array Redis::info()
*/
PHP_METHOD(Redis, info) {
zval *object;
RedisSock *redis_sock;
char *cmd, *opt = NULL;
int cmd_len, opt_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|s",
&object, redis_ce, &opt, &opt_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Build a standalone INFO command or one with an option
if(opt != NULL) {
cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, opt_len);
} else {
cmd_len = redis_cmd_format_static(&cmd, "INFO", "");
}
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_info_response);
}
/* }}} */
/* {{{ proto string Redis::resetStat()
*/
PHP_METHOD(Redis, resetStat)
{
char *cmd;
int cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "s", "RESETSTAT", 9);
generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len);
}
/* }}} */
/* {{{ proto bool Redis::select(long dbNumber)
*/
PHP_METHOD(Redis, select) {
zval *object;
RedisSock *redis_sock;
char *cmd;
int cmd_len;
long dbNumber;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
&object, redis_ce, &dbNumber) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
redis_sock->dbNumber = dbNumber;
cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", dbNumber);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
/* }}} */
/* {{{ proto bool Redis::move(string key, long dbindex)
*/
PHP_METHOD(Redis, move) {
zval *object;
RedisSock *redis_sock;
char *cmd, *key;
int cmd_len, key_len, key_free;
long dbNumber;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl",
&object, redis_ce, &key, &key_len, &dbNumber) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "MOVE", "sd", key, key_len, dbNumber);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_1_response);
}
/* }}} */
PHPAPI void
generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *, zval *, void *)) {
zval *object;
RedisSock *redis_sock;
char *cmd = NULL, *p = NULL;
int cmd_len = 0, argc = 0, kw_len = strlen(kw);
int step = 0; // 0: compute size; 1: copy strings.
zval *z_array;
HashTable *keytable;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
&object, redis_ce, &z_array) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
if(zend_hash_num_elements(Z_ARRVAL_P(z_array)) == 0) {
RETURN_FALSE;
}
for(step = 0; step < 2; ++step) {
if(step == 1) {
cmd_len += 1 + integer_length(1 + 2 * argc) + 2; /* star + arg count + NL */
cmd_len += 1 + integer_length(kw_len) + 2; /* dollar + strlen(kw) + NL */
cmd_len += kw_len + 2; /* kw + NL */
p = cmd = emalloc(cmd_len + 1); /* alloc */
p += sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1 + 2 * argc, kw_len, kw); /* copy header */
}
keytable = Z_ARRVAL_P(z_array);
for(zend_hash_internal_pointer_reset(keytable);
zend_hash_has_more_elements(keytable) == SUCCESS;
zend_hash_move_forward(keytable)) {
char *key, *val;
unsigned int key_len;
int val_len;
unsigned long idx;
int type;
zval **z_value_pp;
int val_free, key_free;
char buf[32];
type = zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, NULL);
if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) {
continue; /* this should never happen, according to the PHP people. */
}
// If the key isn't a string, use the index value returned when grabbing the
// key. We typecast to long, because they could actually be negative.
if(type != HASH_KEY_IS_STRING) {
// Create string representation of our index
key_len = snprintf(buf, sizeof(buf), "%ld", (long)idx);
key = (char*)buf;
} else if(key_len > 0) {
// When not an integer key, the length will include the \0
key_len--;
}
if(step == 0)
argc++; /* found a valid arg */
val_free = redis_serialize(redis_sock, *z_value_pp, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len TSRMLS_CC);
if(step == 0) { /* counting */
cmd_len += 1 + integer_length(key_len) + 2
+ key_len + 2
+ 1 + integer_length(val_len) + 2
+ val_len + 2;
} else {
p += sprintf(p, "$%d" _NL, key_len); /* key len */
memcpy(p, key, key_len); p += key_len; /* key */
memcpy(p, _NL, 2); p += 2;
p += sprintf(p, "$%d" _NL, val_len); /* val len */
memcpy(p, val, val_len); p += val_len; /* val */
memcpy(p, _NL, 2); p += 2;
}
if(val_free) efree(val);
if(key_free) efree(key);
}
}
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(fun);
}
/* {{{ proto bool Redis::mset(array (key => value, ...))
*/
PHP_METHOD(Redis, mset) {
generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", redis_boolean_response);
}
/* }}} */
/* {{{ proto bool Redis::msetnx(array (key => value, ...))
*/
PHP_METHOD(Redis, msetnx) {
generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", redis_1_response);
}
/* }}} */
PHPAPI void common_rpoplpush(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *srckey, int srckey_len, char *dstkey, int dstkey_len, int timeout) {
char *cmd;
int cmd_len;
int srckey_free = redis_key_prefix(redis_sock, &srckey, &srckey_len TSRMLS_CC);
int dstkey_free = redis_key_prefix(redis_sock, &dstkey, &dstkey_len TSRMLS_CC);
if(timeout < 0) {
cmd_len = redis_cmd_format_static(&cmd, "RPOPLPUSH", "ss", srckey, srckey_len, dstkey, dstkey_len);
} else {
cmd_len = redis_cmd_format_static(&cmd, "BRPOPLPUSH", "ssd", srckey, srckey_len, dstkey, dstkey_len, timeout);
}
if(srckey_free) efree(srckey);
if(dstkey_free) efree(dstkey);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_string_response);
}
/* {{{ proto string Redis::rpoplpush(string srckey, string dstkey)
*/
PHP_METHOD(Redis, rpoplpush)
{
zval *object;
RedisSock *redis_sock;
char *srckey = NULL, *dstkey = NULL;
int srckey_len, dstkey_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
&object, redis_ce, &srckey, &srckey_len,
&dstkey, &dstkey_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
common_rpoplpush(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, srckey, srckey_len, dstkey, dstkey_len, -1);
}
/* }}} */
/* {{{ proto string Redis::brpoplpush(string srckey, string dstkey)
*/
PHP_METHOD(Redis, brpoplpush)
{
zval *object;
RedisSock *redis_sock;
char *srckey = NULL, *dstkey = NULL;
int srckey_len, dstkey_len;
long timeout = 0;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ossl",
&object, redis_ce, &srckey, &srckey_len,
&dstkey, &dstkey_len, &timeout) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
common_rpoplpush(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, srckey, srckey_len, dstkey, dstkey_len, timeout);
}
/* }}} */
/* {{{ proto long Redis::zAdd(string key, int score, string value)
*/
PHP_METHOD(Redis, zAdd) {
RedisSock *redis_sock;
char *cmd;
int cmd_len, key_len, val_len;
double score;
char *key, *val;
int val_free, key_free = 0;
char *dbl_str;
int dbl_len;
zval **z_args;
int argc = ZEND_NUM_ARGS(), i;
/* get redis socket */
if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
z_args = emalloc(argc * sizeof(zval*));
if(zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
efree(z_args);
RETURN_FALSE;
}
/* need key, score, value, [score, value...] */
if(argc > 1) {
convert_to_string(z_args[0]); // required string
}
if(argc < 3 || Z_TYPE_P(z_args[0]) != IS_STRING || (argc-1) % 2 != 0) {
efree(z_args);
RETURN_FALSE;
}
/* possibly serialize key */
key = Z_STRVAL_P(z_args[0]);
key_len = Z_STRLEN_P(z_args[0]);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
/* start building the command */
smart_str buf = {0};
smart_str_appendc(&buf, '*');
smart_str_append_long(&buf, argc + 1); /* +1 for ZADD command */
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
/* add command name */
smart_str_appendc(&buf, '$');
smart_str_append_long(&buf, 4);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
smart_str_appendl(&buf, "ZADD", 4);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
/* add key */
smart_str_appendc(&buf, '$');
smart_str_append_long(&buf, key_len);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
smart_str_appendl(&buf, key, key_len);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
for(i = 1; i < argc; i +=2) {
convert_to_double(z_args[i]); // convert score to double
val_free = redis_serialize(redis_sock, z_args[i+1], &val, &val_len TSRMLS_CC); // possibly serialize value.
/* add score */
score = Z_DVAL_P(z_args[i]);
REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, score)
smart_str_appendc(&buf, '$');
smart_str_append_long(&buf, dbl_len);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
smart_str_appendl(&buf, dbl_str, dbl_len);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
efree(dbl_str);
/* add value */
smart_str_appendc(&buf, '$');
smart_str_append_long(&buf, val_len);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
smart_str_appendl(&buf, val, val_len);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
if(val_free) efree(val);
}
/* end string */
smart_str_0(&buf);
cmd = buf.c;
cmd_len = buf.len;
if(key_free) efree(key);
/* cleanup */
efree(z_args);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto array Redis::zRange(string key, int start , int end, bool withscores = FALSE)
*/
PHP_METHOD(Redis, zRange)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
long start, end;
long withscores = 0;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll|b",
&object, redis_ce,
&key, &key_len, &start, &end, &withscores) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
if(withscores) {
cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdds", key, key_len, start, end, "WITHSCORES", 10);
} else {
cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdd", key, key_len, start, end);
}
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
if(withscores) {
IF_ATOMIC() {
redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
} else {
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
}
/* }}} */
/* {{{ proto long Redis::zDelete(string key, string member)
*/
PHP_METHOD(Redis, zDelete)
{
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"ZREM", sizeof("ZREM") - 1,
2, &redis_sock, 0, 0, 1))
return;
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto long Redis::zDeleteRangeByScore(string key, string start, string end)
*/
PHP_METHOD(Redis, zDeleteRangeByScore)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
char *start, *end;
int start_len, end_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osss",
&object, redis_ce,
&key, &key_len, &start, &start_len, &end, &end_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYSCORE", "sss", key, key_len, start, start_len, end, end_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto long Redis::zDeleteRangeByRank(string key, long start, long end)
*/
PHP_METHOD(Redis, zDeleteRangeByRank)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
long start, end;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll",
&object, redis_ce,
&key, &key_len, &start, &end) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYRANK", "sdd", key, key_len, (int)start, (int)end);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto array Redis::zReverseRange(string key, int start , int end, bool withscores = FALSE)
*/
PHP_METHOD(Redis, zReverseRange)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
long start, end;
long withscores = 0;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll|b",
&object, redis_ce,
&key, &key_len, &start, &end, &withscores) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
if(withscores) {
cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdds", key, key_len, start, end, "WITHSCORES", 10);
} else {
cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdd", key, key_len, start, end);
}
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
if(withscores) {
IF_ATOMIC() {
redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
} else {
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
}
/* }}} */
PHPAPI void
redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
zval *object, *z_options = NULL, **z_limit_val_pp = NULL, **z_withscores_val_pp = NULL;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
zend_bool withscores = 0;
char *start, *end;
int start_len, end_len;
int has_limit = 0;
long limit_low, limit_high;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osss|a",
&object, redis_ce,
&key, &key_len,
&start, &start_len,
&end, &end_len,
&z_options) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
/* options */
if (z_options && Z_TYPE_P(z_options) == IS_ARRAY) {
/* add scores */
zend_hash_find(Z_ARRVAL_P(z_options), "withscores", sizeof("withscores"), (void**)&z_withscores_val_pp);
withscores = (z_withscores_val_pp ? Z_BVAL_PP(z_withscores_val_pp) : 0);
/* limit offset, count:
z_limit_val_pp points to an array($longFrom, $longCount)
*/
if(zend_hash_find(Z_ARRVAL_P(z_options), "limit", sizeof("limit"), (void**)&z_limit_val_pp)== SUCCESS) {;
if(zend_hash_num_elements(Z_ARRVAL_PP(z_limit_val_pp)) == 2) {
zval **z_offset_pp, **z_count_pp;
// get the two values from the table, check that they are indeed of LONG type
if(SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_limit_val_pp), 0, (void**)&z_offset_pp) &&
SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_limit_val_pp), 1, (void**)&z_count_pp) &&
Z_TYPE_PP(z_offset_pp) == IS_LONG &&
Z_TYPE_PP(z_count_pp) == IS_LONG) {
has_limit = 1;
limit_low = Z_LVAL_PP(z_offset_pp);
limit_high = Z_LVAL_PP(z_count_pp);
}
}
}
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
if(withscores) {
if(has_limit) {
cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdds",
key, key_len, start, start_len, end, end_len, "LIMIT", 5, limit_low, limit_high, "WITHSCORES", 10);
} else {
cmd_len = redis_cmd_format_static(&cmd, keyword, "ssss",
key, key_len, start, start_len, end, end_len, "WITHSCORES", 10);
}
} else {
if(has_limit) {
cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdd",
key, key_len, start, start_len, end, end_len, "LIMIT", 5, limit_low, limit_high);
} else {
cmd_len = redis_cmd_format_static(&cmd, keyword, "sss", key, key_len, start, start_len, end, end_len);
}
}
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
if(withscores) {
/* with scores! we have to transform the return array.
* return_value currently holds this: [elt0, val0, elt1, val1 ... ]
* we want [elt0 => val0, elt1 => val1], etc.
*/
IF_ATOMIC() {
if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
} else {
IF_ATOMIC() {
if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
}
/* {{{ proto array Redis::zRangeByScore(string key, string start , string end [,array options = NULL])
*/
PHP_METHOD(Redis, zRangeByScore)
{
redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE");
}
/* }}} */
/* {{{ proto array Redis::zRevRangeByScore(string key, string start , string end [,array options = NULL])
*/
PHP_METHOD(Redis, zRevRangeByScore)
{
redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE");
}
/* {{{ proto array Redis::zCount(string key, string start , string end)
*/
PHP_METHOD(Redis, zCount)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
char *start, *end;
int start_len, end_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osss",
&object, redis_ce,
&key, &key_len,
&start, &start_len,
&end, &end_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "ZCOUNT", "sss", key, key_len, start, start_len, end, end_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto long Redis::zCard(string key)
*/
PHP_METHOD(Redis, zCard)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "ZCARD", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto double Redis::zScore(string key, mixed member)
*/
PHP_METHOD(Redis, zScore)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *val = NULL, *cmd;
int key_len, val_len, cmd_len;
int val_free, key_free = 0;
zval *z_value;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz",
&object, redis_ce, &key, &key_len,
&z_value) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_bulk_double_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_bulk_double_response);
}
/* }}} */
PHPAPI void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *val = NULL, *cmd;
int key_len, val_len, cmd_len;
int val_free, key_free = 0;
zval *z_value;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz",
&object, redis_ce, &key, &key_len,
&z_value) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* {{{ proto long Redis::zRank(string key, string member)
*/
PHP_METHOD(Redis, zRank) {
generic_rank_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANK", 5);
}
/* }}} */
/* {{{ proto long Redis::zRevRank(string key, string member)
*/
PHP_METHOD(Redis, zRevRank) {
generic_rank_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANK", 8);
}
/* }}} */
PHPAPI void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd, *val;
int key_len, val_len, cmd_len;
double add;
int val_free, key_free;
zval *z_value;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osdz",
&object, redis_ce,
&key, &key_len, &add, &z_value) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs", key, key_len, add, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_bulk_double_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_bulk_double_response);
}
/* {{{ proto double Redis::zIncrBy(string key, double value, mixed member)
*/
PHP_METHOD(Redis, zIncrBy)
{
generic_incrby_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZINCRBY", sizeof("ZINCRBY")-1);
}
/* }}} */
PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int command_len) {
zval *object, *z_keys, *z_weights = NULL, **z_data;
HashTable *ht_keys, *ht_weights = NULL;
RedisSock *redis_sock;
smart_str cmd = {0};
HashPosition ptr;
char *store_key, *agg_op = NULL;
int cmd_arg_count = 2, store_key_len, agg_op_len = 0, keys_count;
// Grab our parameters
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|a!s",
&object, redis_ce, &store_key, &store_key_len,
&z_keys, &z_weights, &agg_op, &agg_op_len) == FAILURE)
{
RETURN_FALSE;
}
// We'll need our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Grab our keys argument as an array
ht_keys = Z_ARRVAL_P(z_keys);
// Nothing to do if there aren't any keys
if((keys_count = zend_hash_num_elements(ht_keys)) == 0) {
RETURN_FALSE;
} else {
// Increment our overall argument count
cmd_arg_count += keys_count;
}
// Grab and validate our weights array
if(z_weights != NULL) {
ht_weights = Z_ARRVAL_P(z_weights);
// This command is invalid if the weights array isn't the same size
// as our keys array.
if(zend_hash_num_elements(ht_weights) != keys_count) {
RETURN_FALSE;
}
// Increment our overall argument count by the number of keys
// plus one, for the "WEIGHTS" argument itself
cmd_arg_count += keys_count + 1;
}
// AGGREGATE option
if(agg_op_len != 0) {
// Verify our aggregation option
if(strncasecmp(agg_op, "SUM", sizeof("SUM")) &&
strncasecmp(agg_op, "MIN", sizeof("MIN")) &&
strncasecmp(agg_op, "MAX", sizeof("MAX")))
{
RETURN_FALSE;
}
// Two more arguments: "AGGREGATE" and agg_op
cmd_arg_count += 2;
}
// Command header
redis_cmd_init_sstr(&cmd, cmd_arg_count, command, command_len);
// Prefix our key if necessary and add the output key
int key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len TSRMLS_CC);
redis_cmd_append_sstr(&cmd, store_key, store_key_len);
if(key_free) efree(store_key);
// Number of input keys argument
redis_cmd_append_sstr_int(&cmd, keys_count);
// Process input keys
for(zend_hash_internal_pointer_reset_ex(ht_keys, &ptr);
zend_hash_get_current_data_ex(ht_keys, (void**)&z_data, &ptr)==SUCCESS;
zend_hash_move_forward_ex(ht_keys, &ptr))
{
char *key;
int key_free, key_len;
zval *z_tmp = NULL;
if(Z_TYPE_PP(z_data) == IS_STRING) {
key = Z_STRVAL_PP(z_data);
key_len = Z_STRLEN_PP(z_data);
} else {
MAKE_STD_ZVAL(z_tmp);
*z_tmp = **z_data;
convert_to_string(z_tmp);
key = Z_STRVAL_P(z_tmp);
key_len = Z_STRLEN_P(z_tmp);
}
// Apply key prefix if necessary
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
// Append this input set
redis_cmd_append_sstr(&cmd, key, key_len);
// Free our key if it was prefixed
if(key_free) efree(key);
// Free our temporary z_val if it was converted
if(z_tmp) {
zval_dtor(z_tmp);
efree(z_tmp);
z_tmp = NULL;
}
}
// Weights
if(ht_weights != NULL) {
// Append "WEIGHTS" argument
redis_cmd_append_sstr(&cmd, "WEIGHTS", sizeof("WEIGHTS") - 1);
// Process weights
for(zend_hash_internal_pointer_reset_ex(ht_weights, &ptr);
zend_hash_get_current_data_ex(ht_weights, (void**)&z_data, &ptr)==SUCCESS;
zend_hash_move_forward_ex(ht_weights, &ptr))
{
// Ignore non numeric arguments, unless they're special Redis numbers
if (Z_TYPE_PP(z_data) != IS_LONG && Z_TYPE_PP(z_data) != IS_DOUBLE &&
strncasecmp(Z_STRVAL_PP(z_data), "inf", sizeof("inf")) != 0 &&
strncasecmp(Z_STRVAL_PP(z_data), "-inf", sizeof("-inf")) != 0 &&
strncasecmp(Z_STRVAL_PP(z_data), "+inf", sizeof("+inf")) != 0)
{
// We should abort if we have an invalid weight, rather than pass
// a different number of weights than the user is expecting
efree(cmd.c);
RETURN_FALSE;
}
// Append the weight based on the input type
switch(Z_TYPE_PP(z_data)) {
case IS_LONG:
redis_cmd_append_sstr_long(&cmd, Z_LVAL_PP(z_data));
break;
case IS_DOUBLE:
redis_cmd_append_sstr_dbl(&cmd, Z_DVAL_PP(z_data));
break;
case IS_STRING:
redis_cmd_append_sstr(&cmd, Z_STRVAL_PP(z_data), Z_STRLEN_PP(z_data));
break;
}
}
}
// Aggregation options, if we have them
if(agg_op_len != 0) {
redis_cmd_append_sstr(&cmd, "AGGREGATE", sizeof("AGGREGATE") - 1);
redis_cmd_append_sstr(&cmd, agg_op, agg_op_len);
}
// Kick off our request
REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* zInter */
PHP_METHOD(Redis, zInter) {
generic_z_command(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZINTERSTORE", 11);
}
/* zUnion */
PHP_METHOD(Redis, zUnion) {
generic_z_command(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZUNIONSTORE", 11);
}
/* hashes */
PHPAPI void
generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *, zval *, void *)) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd, *member, *val;
int key_len, member_len, cmd_len, val_len;
int val_free, key_free = 0;
zval *z_value;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ossz",
&object, redis_ce,
&key, &key_len, &member, &member_len, &z_value) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, kw, "sss", key, key_len, member, member_len, val, val_len);
if(val_free) efree(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(fun);
}
/* hSet */
PHP_METHOD(Redis, hSet)
{
generic_hset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HSET", redis_long_response);
}
/* }}} */
/* hSetNx */
PHP_METHOD(Redis, hSetNx)
{
generic_hset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HSETNX", redis_1_response);
}
/* }}} */
/* hGet */
PHP_METHOD(Redis, hGet)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd, *member;
int key_len, member_len, cmd_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
&object, redis_ce,
&key, &key_len, &member, &member_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "HGET", "ss", key, key_len, member, member_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_string_response);
}
/* }}} */
/* hLen */
PHP_METHOD(Redis, hLen)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "HLEN", "s", key, key_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
PHPAPI RedisSock*
generic_hash_command_2(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len, char **out_cmd, int *out_len) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd, *member;
int key_len, cmd_len, member_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
&object, redis_ce,
&key, &key_len, &member, &member_len) == FAILURE) {
ZVAL_BOOL(return_value, 0);
return NULL;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
ZVAL_BOOL(return_value, 0);
return NULL;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, member, member_len);
if(key_free) efree(key);
*out_cmd = cmd;
*out_len = cmd_len;
return redis_sock;
}
/* hDel */
PHP_METHOD(Redis, hDel)
{
RedisSock *redis_sock;
if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
"HDEL", sizeof("HDEL") - 1,
2, &redis_sock, 0, 0, 0))
return;
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* hExists */
PHP_METHOD(Redis, hExists)
{
char *cmd;
int cmd_len;
RedisSock *redis_sock = generic_hash_command_2(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HEXISTS", 7, &cmd, &cmd_len);
if(!redis_sock)
RETURN_FALSE;
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_1_response);
}
PHPAPI RedisSock*
generic_hash_command_1(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd;
int key_len, cmd_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce,
&key, &key_len) == FAILURE) {
ZVAL_BOOL(return_value, 0);
return NULL;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
ZVAL_BOOL(return_value, 0);
return NULL;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len);
if(key_free) efree(key);
/* call REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) without breaking the return value */
IF_MULTI_OR_ATOMIC() {
if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
efree(cmd);
return NULL;
}
efree(cmd);
}
IF_PIPELINE() {
PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len);
efree(cmd);
}
return redis_sock;
}
/* hKeys */
PHP_METHOD(Redis, hKeys)
{
RedisSock *redis_sock = generic_hash_command_1(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HKEYS", sizeof("HKEYS")-1);
if(!redis_sock)
RETURN_FALSE;
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw);
}
/* hVals */
PHP_METHOD(Redis, hVals)
{
RedisSock *redis_sock = generic_hash_command_1(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HVALS", sizeof("HVALS")-1);
if(!redis_sock)
RETURN_FALSE;
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
}
PHP_METHOD(Redis, hGetAll) {
RedisSock *redis_sock = generic_hash_command_1(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HGETALL", sizeof("HGETALL")-1);
if(!redis_sock)
RETURN_FALSE;
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped_strings);
}
PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC) {
zval *z_ret;
HashTable *keytable;
MAKE_STD_ZVAL(z_ret);
array_init(z_ret);
keytable = Z_ARRVAL_P(z_tab);
for(zend_hash_internal_pointer_reset(keytable);
zend_hash_has_more_elements(keytable) == SUCCESS;
zend_hash_move_forward(keytable)) {
char *tablekey, *hkey, *hval;
unsigned int tablekey_len;
int hkey_len;
unsigned long idx;
zval **z_key_pp, **z_value_pp;
zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL);
if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) {
continue; /* this should never happen, according to the PHP people. */
}
/* get current value, a key */
convert_to_string(*z_key_pp);
hkey = Z_STRVAL_PP(z_key_pp);
hkey_len = Z_STRLEN_PP(z_key_pp);
/* move forward */
zend_hash_move_forward(keytable);
/* fetch again */
zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL);
if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) {
continue; /* this should never happen, according to the PHP people. */
}
/* get current value, a hash value now. */
hval = Z_STRVAL_PP(z_value_pp);
if(use_atof) { /* zipping a score */
add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval));
} else { /* add raw copy */
zval *z = NULL;
MAKE_STD_ZVAL(z);
*z = **z_value_pp;
zval_copy_ctor(z);
add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z);
}
}
/* replace */
zval_dtor(z_tab);
*z_tab = *z_ret;
zval_copy_ctor(z_tab);
zval_dtor(z_ret);
efree(z_ret);
}
PHP_METHOD(Redis, hIncrByFloat)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd, *member;
int key_len, member_len, cmd_len, key_free;
double val;
// Validate we have the right number of arguments
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ossd",
&object, redis_ce,
&key, &key_len, &member, &member_len, &val) == FAILURE) {
RETURN_FALSE;
}
// Grab our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "HINCRBYFLOAT", "ssf", key, key_len, member, member_len, val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_bulk_double_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_bulk_double_response);
}
PHP_METHOD(Redis, hIncrBy)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd, *member, *val;
int key_len, member_len, cmd_len, val_len, key_free;
int i;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osss",
&object, redis_ce,
&key, &key_len, &member, &member_len, &val, &val_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
/* check for validity of numeric string */
i = 0;
if(val_len && val[0] == '-') { /* negative case */
i++;
}
for(; i < val_len; ++i) {
if(val[i] < '0' || val[i] > '9') {
RETURN_FALSE;
}
}
/* HINCRBY key member amount */
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "HINCRBY", "sss", key, key_len, member, member_len, val, val_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* {{{ array Redis::hMget(string hash, array keys) */
PHP_METHOD(Redis, hMget) {
zval *object;
RedisSock *redis_sock;
char *key = NULL;
zval *z_array, **z_keys, **data;
int field_count, i, valid, key_len, key_free;
HashTable *ht_array;
HashPosition ptr;
smart_str cmd = {0};
// Make sure we can grab our arguments properly
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa",
&object, redis_ce, &key, &key_len, &z_array)
== FAILURE)
{
RETURN_FALSE;
}
// We'll need our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Grab member count and abort if we don't have any
if((field_count = zend_hash_num_elements(Z_ARRVAL_P(z_array))) == 0) {
RETURN_FALSE;
}
// Prefix our key if we need to
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
// Allocate enough memory for the number of keys being requested
z_keys = ecalloc(field_count, sizeof(zval *));
// Grab our HashTable
ht_array = Z_ARRVAL_P(z_array);
// Iterate through our keys, grabbing members that are valid
for(valid=0, zend_hash_internal_pointer_reset_ex(ht_array, &ptr);
zend_hash_get_current_data_ex(ht_array, (void**)&data, &ptr)==SUCCESS;
zend_hash_move_forward_ex(ht_array, &ptr))
{
// Make sure the data is a long or string, and if it's a string that
// it isn't empty. There is no reason to send empty length members.
if((Z_TYPE_PP(data) == IS_STRING && Z_STRLEN_PP(data)>0) ||
Z_TYPE_PP(data) == IS_LONG)
{
// This is a key we can ask for, copy it and set it in our array
MAKE_STD_ZVAL(z_keys[valid]);
*z_keys[valid] = **data;
zval_copy_ctor(z_keys[valid]);
convert_to_string(z_keys[valid]);
// Increment the number of valid keys we've encountered
valid++;
}
}
// If we don't have any valid keys, we can abort here
if(valid == 0) {
if(key_free) efree(key);
efree(z_keys);
RETURN_FALSE;
}
// Build command header. One extra argument for the hash key itself
redis_cmd_init_sstr(&cmd, valid+1, "HMGET", sizeof("HMGET")-1);
// Add the hash key
redis_cmd_append_sstr(&cmd, key, key_len);
// Free key memory if it was prefixed
if(key_free) efree(key);
// Iterate our keys, appending them as arguments
for(i=0;i<valid;i++) {
redis_cmd_append_sstr(&cmd, Z_STRVAL_P(z_keys[i]), Z_STRLEN_P(z_keys[i]));
}
// Kick off our request
REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
IF_ATOMIC() {
redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, z_keys);
}
REDIS_PROCESS_RESPONSE_CLOSURE(redis_sock_read_multibulk_reply_assoc, z_keys);
}
PHP_METHOD(Redis, hMset)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *cmd, *old_cmd = NULL;
int key_len, cmd_len, key_free, i, element_count = 2;
zval *z_hash;
HashTable *ht_hash;
smart_str set_cmds = {0};
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa",
&object, redis_ce,
&key, &key_len, &z_hash) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
ht_hash = Z_ARRVAL_P(z_hash);
if (zend_hash_num_elements(ht_hash) == 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format(&cmd,
"$5" _NL "HMSET" _NL
"$%d" _NL "%s" _NL
, key_len, key, key_len);
if(key_free) efree(key);
/* looping on each item of the array */
for(i =0, zend_hash_internal_pointer_reset(ht_hash);
zend_hash_has_more_elements(ht_hash) == SUCCESS;
i++, zend_hash_move_forward(ht_hash)) {
char *hkey, hkey_str[40];
unsigned int hkey_len;
unsigned long idx;
int type;
zval **z_value_p;
char *hval;
int hval_len, hval_free;
type = zend_hash_get_current_key_ex(ht_hash, &hkey, &hkey_len, &idx, 0, NULL);
if(zend_hash_get_current_data(ht_hash, (void**)&z_value_p) == FAILURE) {
continue; /* this should never happen */
}
if(type != HASH_KEY_IS_STRING) { /* convert to string */
hkey_len = 1 + sprintf(hkey_str, "%ld", idx);
hkey = (char*)hkey_str;
}
element_count += 2;
/* key is set. */
hval_free = redis_serialize(redis_sock, *z_value_p, &hval, &hval_len TSRMLS_CC);
// Append our member and value in place
redis_cmd_append_sstr(&set_cmds, hkey, hkey_len - 1);
redis_cmd_append_sstr(&set_cmds, hval, hval_len);
if(hval_free) efree(hval);
}
// Now construct the entire command
old_cmd = cmd;
cmd_len = redis_cmd_format(&cmd, "*%d" _NL "%s%s", element_count, cmd, cmd_len, set_cmds.c, set_cmds.len);
efree(old_cmd);
// Free the HMSET bits
efree(set_cmds.c);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) {
char *response;
int response_len, ret = 0;
if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) {
return 0;
}
if(strncmp(response, "+QUEUED", 7) == 0) {
ret = 1;
}
efree(response);
return ret;
}
/* flag : get, set {ATOMIC, MULTI, PIPELINE} */
PHP_METHOD(Redis, multi)
{
RedisSock *redis_sock;
char *cmd;
int response_len, cmd_len;
char * response;
zval *object;
long multi_value = MULTI;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, 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_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
if(multi_value == MULTI || multi_value == PIPELINE) {
redis_sock->mode = multi_value;
} else {
RETURN_FALSE;
}
redis_sock->current = NULL;
IF_MULTI() {
cmd_len = redis_cmd_format_static(&cmd, "MULTI", "");
if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
efree(cmd);
RETURN_FALSE;
}
efree(cmd);
if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) {
RETURN_FALSE;
}
if(strncmp(response, "+OK", 3) == 0) {
efree(response);
RETURN_ZVAL(getThis(), 1, 0);
}
efree(response);
RETURN_FALSE;
}
IF_PIPELINE() {
free_reply_callbacks(getThis(), redis_sock);
RETURN_ZVAL(getThis(), 1, 0);
}
}
/* discard */
PHP_METHOD(Redis, discard)
{
RedisSock *redis_sock;
zval *object;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
&object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
redis_sock->mode = ATOMIC;
redis_send_discard(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
}
PHPAPI int redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock)
{
zval *z_tab;
MAKE_STD_ZVAL(z_tab);
array_init(z_tab);
redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, z_tab, 0);
*return_value = *z_tab;
efree(z_tab);
/* free allocated function/request memory */
free_reply_callbacks(getThis(), redis_sock);
return 0;
}
/* redis_sock_read_multibulk_multi_reply */
PHPAPI int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS,
RedisSock *redis_sock)
{
char inbuf[1024];
int numElems;
zval *z_tab;
redis_check_eof(redis_sock TSRMLS_CC);
php_stream_gets(redis_sock->stream, inbuf, 1024);
if(inbuf[0] != '*') {
return -1;
}
/* number of responses */
numElems = atoi(inbuf+1);
if(numElems < 0) {
return -1;
}
zval_dtor(return_value);
MAKE_STD_ZVAL(z_tab);
array_init(z_tab);
redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, z_tab, numElems);
*return_value = *z_tab;
efree(z_tab);
return 0;
}
void
free_reply_callbacks(zval *z_this, RedisSock *redis_sock) {
fold_item *fi;
fold_item *head = redis_sock->head;
request_item *ri;
for(fi = head; fi; ) {
fold_item *fi_next = fi->next;
free(fi);
fi = fi_next;
}
redis_sock->head = NULL;
redis_sock->current = NULL;
for(ri = redis_sock->pipeline_head; ri; ) {
struct request_item *ri_next = ri->next;
free(ri->request_str);
free(ri);
ri = ri_next;
}
redis_sock->pipeline_head = NULL;
redis_sock->pipeline_current = NULL;
}
/* exec */
PHP_METHOD(Redis, exec)
{
RedisSock *redis_sock;
char *cmd;
int cmd_len;
zval *object;
struct request_item *ri;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
&object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
IF_MULTI() {
cmd_len = redis_cmd_format_static(&cmd, "EXEC", "");
if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
efree(cmd);
RETURN_FALSE;
}
efree(cmd);
if (redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0) {
zval_dtor(return_value);
free_reply_callbacks(object, redis_sock);
redis_sock->mode = ATOMIC;
redis_sock->watching = 0;
RETURN_FALSE;
}
free_reply_callbacks(object, redis_sock);
redis_sock->mode = ATOMIC;
redis_sock->watching = 0;
}
IF_PIPELINE() {
char *request = NULL;
int total = 0;
int offset = 0;
/* compute the total request size */
for(ri = redis_sock->pipeline_head; ri; ri = ri->next) {
total += ri->request_size;
}
if(total) {
request = malloc(total);
}
/* concatenate individual elements one by one in the target buffer */
for(ri = redis_sock->pipeline_head; ri; ri = ri->next) {
memcpy(request + offset, ri->request_str, ri->request_size);
offset += ri->request_size;
}
if(request != NULL) {
if (redis_sock_write(redis_sock, request, total TSRMLS_CC) < 0) {
free(request);
free_reply_callbacks(object, redis_sock);
redis_sock->mode = ATOMIC;
RETURN_FALSE;
}
free(request);
} else {
redis_sock->mode = ATOMIC;
free_reply_callbacks(object, redis_sock);
array_init(return_value); /* empty array when no command was run. */
return;
}
if (redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0) {
redis_sock->mode = ATOMIC;
free_reply_callbacks(object, redis_sock);
RETURN_FALSE;
}
redis_sock->mode = ATOMIC;
free_reply_callbacks(object, redis_sock);
}
}
PHPAPI void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, RedisSock *redis_sock, zval *z_tab) {
item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx TSRMLS_CC);
}
PHPAPI int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
RedisSock *redis_sock, zval *z_tab, int numElems)
{
fold_item *head = redis_sock->head;
fold_item *current = redis_sock->current;
for(current = head; current; current = current->next) {
fold_this_item(INTERNAL_FUNCTION_PARAM_PASSTHRU, current, redis_sock, z_tab);
}
redis_sock->current = current;
return 0;
}
PHP_METHOD(Redis, pipeline)
{
RedisSock *redis_sock;
zval *object;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
&object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
/* if the flag is activated, send the command, the reply will be "QUEUED" or -ERR */
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
redis_sock->mode = PIPELINE;
/*
NB : we keep the function fold, to detect the last function.
We need the response format of the n - 1 command. So, we can delete when n > 2, the { 1 .. n - 2} commands
*/
free_reply_callbacks(getThis(), redis_sock);
RETURN_ZVAL(getThis(), 1, 0);
}
/*
publish channel message
@return the number of subscribers
*/
PHP_METHOD(Redis, publish)
{
zval *object;
RedisSock *redis_sock;
char *cmd, *key, *val;
int cmd_len, key_len, val_len, key_free;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
&object, redis_ce,
&key, &key_len, &val, &val_len) == FAILURE) {
RETURN_NULL();
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "PUBLISH", "ss", key, key_len, val, val_len);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd)
{
zval *object, *array, **data;
HashTable *arr_hash;
HashPosition pointer;
RedisSock *redis_sock;
char *cmd = "", *old_cmd = NULL, *key;
int cmd_len, array_count, key_len, key_free;
zval *z_tab, **tmp;
char *type_response;
// Function call information
zend_fcall_info z_callback;
zend_fcall_info_cache z_callback_cache;
zval *z_ret, **z_args[4];
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oaf",
&object, redis_ce, &array, &z_callback, &z_callback_cache) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
arr_hash = Z_ARRVAL_P(array);
array_count = zend_hash_num_elements(arr_hash);
if (array_count == 0) {
RETURN_FALSE;
}
for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer);
zend_hash_get_current_data_ex(arr_hash, (void**) &data,
&pointer) == SUCCESS;
zend_hash_move_forward_ex(arr_hash, &pointer)) {
if (Z_TYPE_PP(data) == IS_STRING) {
char *old_cmd = NULL;
if(*cmd) {
old_cmd = cmd;
}
// Grab our key and len
key = Z_STRVAL_PP(data);
key_len = Z_STRLEN_PP(data);
// Prefix our key if neccisary
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = spprintf(&cmd, 0, "%s %s", cmd, key);
if(old_cmd) {
efree(old_cmd);
}
// Free our key if it was prefixed
if(key_free) {
efree(key);
}
}
}
old_cmd = cmd;
cmd_len = spprintf(&cmd, 0, "%s %s\r\n", sub_cmd, cmd);
efree(old_cmd);
if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
efree(cmd);
RETURN_FALSE;
}
efree(cmd);
/* read the status of the execution of the command `subscribe` */
z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
if(z_tab == NULL) {
RETURN_FALSE;
}
if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&tmp) == SUCCESS) {
type_response = Z_STRVAL_PP(tmp);
if(strcmp(type_response, sub_cmd) != 0) {
efree(tmp);
efree(z_tab);
RETURN_FALSE;
}
} else {
efree(z_tab);
RETURN_FALSE;
}
efree(z_tab);
// Set a pointer to our return value and to our arguments.
z_callback.retval_ptr_ptr = &z_ret;
z_callback.params = z_args;
z_callback.no_separation = 0;
/* Multibulk Response, format : {message type, originating channel, message payload} */
while(1) {
/* call the callback with this z_tab in argument */
zval **type, **channel, **pattern, **data;
z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
int is_pmsg, tab_idx = 1;
if(z_tab == NULL || Z_TYPE_P(z_tab) != IS_ARRAY) {
//ERROR
break;
}
if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&type) == FAILURE || Z_TYPE_PP(type) != IS_STRING) {
break;
}
// Make sure we have a message or pmessage
if(!strncmp(Z_STRVAL_PP(type), "message", 7) || !strncmp(Z_STRVAL_PP(type), "pmessage", 8)) {
// Is this a pmessage
is_pmsg = *Z_STRVAL_PP(type) == 'p';
} else {
continue; // It's not a message or pmessage
}
// If this is a pmessage, we'll want to extract the pattern first
if(is_pmsg) {
// Extract pattern
if(zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&pattern) == FAILURE) {
break;
}
}
// Extract channel and data
if (zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&channel) == FAILURE) {
break;
}
if (zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&data) == FAILURE) {
break;
}
// Always pass the Redis object through
z_args[0] = &getThis();
// Set up our callback args depending on the message type
if(is_pmsg) {
z_args[1] = pattern;
z_args[2] = channel;
z_args[3] = data;
} else {
z_args[1] = channel;
z_args[2] = data;
}
// Set our argument information
z_callback.param_count = tab_idx;
// Break if we can't call the function
if(zend_call_function(&z_callback, &z_callback_cache TSRMLS_CC) != SUCCESS) {
break;
}
// If we have a return value, free it. Note, we could use the return value to break the subscribe loop
if(z_ret) zval_ptr_dtor(&z_ret);
/* TODO: provide a way to break out of the loop. */
zval_dtor(z_tab);
efree(z_tab);
}
}
/* {{{ proto void Redis::psubscribe(Array(pattern1, pattern2, ... patternN))
*/
PHP_METHOD(Redis, psubscribe)
{
generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "psubscribe");
}
/* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN))
*/
PHP_METHOD(Redis, subscribe) {
generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "subscribe");
}
/**
* [p]unsubscribe channel_0 channel_1 ... channel_n
* [p]unsubscribe(array(channel_0, channel_1, ..., channel_n))
* response format :
* array(
* channel_0 => TRUE|FALSE,
* channel_1 => TRUE|FALSE,
* ...
* channel_n => TRUE|FALSE
* );
**/
PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd)
{
zval *object, *array, **data;
HashTable *arr_hash;
HashPosition pointer;
RedisSock *redis_sock;
char *cmd = "", *old_cmd = NULL;
int cmd_len, array_count;
int i;
zval *z_tab, **z_channel;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
&object, redis_ce, &array) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
arr_hash = Z_ARRVAL_P(array);
array_count = zend_hash_num_elements(arr_hash);
if (array_count == 0) {
RETURN_FALSE;
}
for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer);
zend_hash_get_current_data_ex(arr_hash, (void**) &data,
&pointer) == SUCCESS;
zend_hash_move_forward_ex(arr_hash, &pointer)) {
if (Z_TYPE_PP(data) == IS_STRING) {
char *old_cmd = NULL;
if(*cmd) {
old_cmd = cmd;
}
cmd_len = spprintf(&cmd, 0, "%s %s", cmd, Z_STRVAL_PP(data));
if(old_cmd) {
efree(old_cmd);
}
}
}
old_cmd = cmd;
cmd_len = spprintf(&cmd, 0, "%s %s\r\n", unsub_cmd, cmd);
efree(old_cmd);
if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
efree(cmd);
RETURN_FALSE;
}
efree(cmd);
i = 1;
array_init(return_value);
while( i <= array_count) {
z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
if(Z_TYPE_P(z_tab) == IS_ARRAY) {
if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) == FAILURE) {
RETURN_FALSE;
}
add_assoc_bool(return_value, Z_STRVAL_PP(z_channel), 1);
} else {
//error
efree(z_tab);
RETURN_FALSE;
}
efree(z_tab);
i ++;
}
}
PHP_METHOD(Redis, unsubscribe)
{
generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNSUBSCRIBE");
}
PHP_METHOD(Redis, punsubscribe)
{
generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUNSUBSCRIBE");
}
/* {{{ proto string Redis::bgrewriteaof()
*/
PHP_METHOD(Redis, bgrewriteaof)
{
char *cmd;
int cmd_len = redis_cmd_format_static(&cmd, "BGREWRITEAOF", "");
generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len);
}
/* }}} */
/* {{{ proto string Redis::slaveof([host, port])
*/
PHP_METHOD(Redis, slaveof)
{
zval *object;
RedisSock *redis_sock;
char *cmd = "", *host = NULL;
int cmd_len, host_len;
long port = 6379;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|sl",
&object, redis_ce, &host, &host_len, &port) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
if(host && host_len) {
cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "sd", host, host_len, (int)port);
} else {
cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "ss", "NO", 2, "ONE", 3);
}
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
/* }}} */
/* {{{ proto string Redis::object(key)
*/
PHP_METHOD(Redis, object)
{
zval *object;
RedisSock *redis_sock;
char *cmd = "", *info = NULL, *key = NULL;
int cmd_len, info_len, key_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
&object, redis_ce, &info, &info_len, &key, &key_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
cmd_len = redis_cmd_format_static(&cmd, "OBJECT", "ss", info, info_len, key, key_len);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
if(info_len == 8 && (strncasecmp(info, "refcount", 8) == 0 || strncasecmp(info, "idletime", 8) == 0)) {
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
} else if(info_len == 8 && strncasecmp(info, "encoding", 8) == 0) {
IF_ATOMIC() {
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_string_response);
} else { /* fail */
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
}
/* }}} */
/* {{{ proto string Redis::getOption($option)
*/
PHP_METHOD(Redis, getOption) {
RedisSock *redis_sock;
zval *object;
long option;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
&object, redis_ce, &option) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
switch(option) {
case REDIS_OPT_SERIALIZER:
RETURN_LONG(redis_sock->serializer);
case REDIS_OPT_PREFIX:
if(redis_sock->prefix) {
RETURN_STRINGL(redis_sock->prefix, redis_sock->prefix_len, 1);
}
RETURN_NULL();
case REDIS_OPT_READ_TIMEOUT:
RETURN_DOUBLE(redis_sock->read_timeout);
default:
RETURN_FALSE;
}
}
/* }}} */
/* {{{ proto string Redis::setOption(string $option, mixed $value)
*/
PHP_METHOD(Redis, setOption) {
RedisSock *redis_sock;
zval *object;
long option, val_long;
char *val_str;
int val_len;
struct timeval read_tv;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols",
&object, redis_ce, &option, &val_str, &val_len) == FAILURE) {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
switch(option) {
case REDIS_OPT_SERIALIZER:
val_long = atol(val_str);
if(val_long == REDIS_SERIALIZER_NONE
#ifdef HAVE_REDIS_IGBINARY
|| val_long == REDIS_SERIALIZER_IGBINARY
#endif
|| val_long == REDIS_SERIALIZER_PHP) {
redis_sock->serializer = val_long;
RETURN_TRUE;
} else {
RETURN_FALSE;
}
break;
case REDIS_OPT_PREFIX:
if(redis_sock->prefix) {
efree(redis_sock->prefix);
}
if(val_len == 0) {
redis_sock->prefix = NULL;
redis_sock->prefix_len = 0;
} else {
redis_sock->prefix_len = val_len;
redis_sock->prefix = ecalloc(1+val_len, 1);
memcpy(redis_sock->prefix, val_str, val_len);
}
RETURN_TRUE;
case REDIS_OPT_READ_TIMEOUT:
redis_sock->read_timeout = atof(val_str);
if(redis_sock->stream) {
read_tv.tv_sec = (time_t)redis_sock->read_timeout;
read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000);
php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT,
0, &read_tv);
}
RETURN_TRUE;
default:
RETURN_FALSE;
}
}
/* }}} */
/* {{{ proto boolean Redis::config(string op, string key [, mixed value])
*/
PHP_METHOD(Redis, config)
{
zval *object;
RedisSock *redis_sock;
char *key = NULL, *val = NULL, *cmd, *op = NULL;
int key_len, val_len, cmd_len, op_len;
enum {CFG_GET, CFG_SET} mode;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss|s",
&object, redis_ce, &op, &op_len, &key, &key_len,
&val, &val_len) == FAILURE) {
RETURN_FALSE;
}
/* op must be GET or SET */
if(strncasecmp(op, "GET", 3) == 0) {
mode = CFG_GET;
} else if(strncasecmp(op, "SET", 3) == 0) {
mode = CFG_SET;
} else {
RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
if (mode == CFG_GET && val == NULL) {
cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss", op, op_len, key, key_len);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
IF_ATOMIC() {
redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped_strings);
} else if(mode == CFG_SET && val != NULL) {
cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
} else {
RETURN_FALSE;
}
}
/* }}} */
/* {{{ proto boolean Redis::slowlog(string arg, [int option])
*/
PHP_METHOD(Redis, slowlog) {
zval *object;
RedisSock *redis_sock;
char *arg, *cmd;
int arg_len, cmd_len;
long option;
enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode;
// Make sure we can get parameters
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l",
&object, redis_ce, &arg, &arg_len, &option) == FAILURE)
{
RETURN_FALSE;
}
// Figure out what kind of slowlog command we're executing
if(!strncasecmp(arg, "GET", 3)) {
mode = SLOWLOG_GET;
} else if(!strncasecmp(arg, "LEN", 3)) {
mode = SLOWLOG_LEN;
} else if(!strncasecmp(arg, "RESET", 5)) {
mode = SLOWLOG_RESET;
} else {
// This command is not valid
RETURN_FALSE;
}
// Make sure we can grab our redis socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Create our command. For everything except SLOWLOG GET (with an arg) it's just two parts
if(mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) {
cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "sl", arg, arg_len, option);
} else {
cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "s", arg, arg_len);
}
// Kick off our command
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
}
// Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter
PHPAPI int
redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) {
zval **elem;
HashTable *args_hash;
HashPosition hash_pos;
int cmd_len, args_count = 0;
int eval_cmd_count = 2;
// If we've been provided arguments, we'll want to include those in our eval command
if(args != NULL) {
// Init our hash array value, and grab the count
args_hash = Z_ARRVAL_P(args);
args_count = zend_hash_num_elements(args_hash);
// We only need to process the arguments if the array is non empty
if(args_count > 0) {
// Header for our EVAL command
cmd_len = redis_cmd_format_header(ret, keyword, eval_cmd_count + args_count);
// Now append the script itself, and the number of arguments to treat as keys
cmd_len = redis_cmd_append_str(ret, cmd_len, value, val_len);
cmd_len = redis_cmd_append_int(ret, cmd_len, keys_count);
// Iterate the values in our "keys" array
for(zend_hash_internal_pointer_reset_ex(args_hash, &hash_pos);
zend_hash_get_current_data_ex(args_hash, (void **)&elem, &hash_pos) == SUCCESS;
zend_hash_move_forward_ex(args_hash, &hash_pos))
{
zval *z_tmp = NULL;
char *key, *old_cmd;
int key_len, key_free;
if(Z_TYPE_PP(elem) == IS_STRING) {
key = Z_STRVAL_PP(elem);
key_len = Z_STRLEN_PP(elem);
} else {
// Convert it to a string
MAKE_STD_ZVAL(z_tmp);
*z_tmp = **elem;
zval_copy_ctor(z_tmp);
convert_to_string(z_tmp);
key = Z_STRVAL_P(z_tmp);
key_len = Z_STRLEN_P(z_tmp);
}
// Keep track of the old command pointer
old_cmd = *ret;
// If this is still a key argument, prefix it if we've been set up to prefix keys
key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC) : 0;
// Append this key to our EVAL command, free our old command
cmd_len = redis_cmd_format(ret, "%s$%d" _NL "%s" _NL, *ret, cmd_len, key_len, key, key_len);
efree(old_cmd);
// Free our key, old command if we need to
if(key_free) efree(key);
// Free our temporary zval (converted from non string) if we've got one
if(z_tmp) {
zval_dtor(z_tmp);
efree(z_tmp);
}
}
}
}
// If there weren't any arguments (none passed, or an empty array), construct a standard no args command
if(args_count < 1) {
cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, val_len, 0);
}
// Return our command length
return cmd_len;
}
/* {{{ proto variant Redis::evalsha(string script_sha1, [array keys, int num_key_args])
*/
PHP_METHOD(Redis, evalsha)
{
zval *object, *args= NULL;
char *cmd, *sha;
int cmd_len, sha_len;
long keys_count = 0;
RedisSock *redis_sock;
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|al",
&object, redis_ce, &sha, &sha_len, &args, &keys_count) == FAILURE) {
RETURN_FALSE;
}
// Attempt to grab socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Construct our EVALSHA command
cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVALSHA", sha, sha_len, args, keys_count TSRMLS_CC);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
}
/* {{{ proto variant Redis::eval(string script, [array keys, int num_key_args])
*/
PHP_METHOD(Redis, eval)
{
zval *object, *args = NULL;
RedisSock *redis_sock;
char *script, *cmd = "";
int script_len, cmd_len;
long keys_count = 0;
// Attempt to parse parameters
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|al",
&object, redis_ce, &script, &script_len, &args, &keys_count) == FAILURE) {
RETURN_FALSE;
}
// Attempt to grab socket
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Construct our EVAL command
cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVAL", script, script_len, args, keys_count TSRMLS_CC);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
}
PHPAPI int
redis_build_script_exists_cmd(char **ret, zval **argv, int argc) {
// Our command length and iterator
int cmd_len = 0, i;
// Start building our command
cmd_len = redis_cmd_format_header(ret, "SCRIPT", argc + 1); // +1 for "EXISTS"
cmd_len = redis_cmd_append_str(ret, cmd_len, "EXISTS", 6);
// Iterate our arguments
for(i=0;i<argc;i++) {
// Convert our argument to a string if we need to
convert_to_string(argv[i]);
// Append this script sha to our SCRIPT EXISTS command
cmd_len = redis_cmd_append_str(ret, cmd_len, Z_STRVAL_P(argv[i]), Z_STRLEN_P(argv[i]));
}
// Success
return cmd_len;
}
/* {{{ proto status Redis::script('flush')
* {{{ proto status Redis::script('kill')
* {{{ proto string Redis::script('load', lua_script)
* {{{ proto int Reids::script('exists', script_sha1 [, script_sha2, ...])
*/
PHP_METHOD(Redis, script) {
zval **z_args;
RedisSock *redis_sock;
int cmd_len, argc;
char *cmd;
// Attempt to grab our socket
if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Grab the number of arguments
argc = ZEND_NUM_ARGS();
// Allocate an array big enough to store our arguments
z_args = emalloc(argc * sizeof(zval*));
// Make sure we can grab our arguments, we have a string directive
if(zend_get_parameters_array(ht, argc, z_args) == FAILURE ||
(argc < 1 || Z_TYPE_P(z_args[0]) != IS_STRING))
{
efree(z_args);
RETURN_FALSE;
}
// Branch based on the directive
if(!strcasecmp(Z_STRVAL_P(z_args[0]), "flush") || !strcasecmp(Z_STRVAL_P(z_args[0]), "kill")) {
// Simple SCRIPT FLUSH, or SCRIPT_KILL command
cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "s", Z_STRVAL_P(z_args[0]), Z_STRLEN_P(z_args[0]));
} else if(!strcasecmp(Z_STRVAL_P(z_args[0]), "load")) {
// Make sure we have a second argument, and it's not empty. If it is
// empty, we can just return an empty array (which is what Redis does)
if(argc < 2 || Z_TYPE_P(z_args[1]) != IS_STRING || Z_STRLEN_P(z_args[1]) < 1) {
// Free our args
efree(z_args);
RETURN_FALSE;
}
// Format our SCRIPT LOAD command
cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "ss", "LOAD", 4, Z_STRVAL_P(z_args[1]), Z_STRLEN_P(z_args[1]));
} else if(!strcasecmp(Z_STRVAL_P(z_args[0]), "exists")) {
// Construct our SCRIPT EXISTS command
cmd_len = redis_build_script_exists_cmd(&cmd, &(z_args[1]), argc-1);
} else {
// Unknown directive
efree(z_args);
RETURN_FALSE;
}
// Free our alocated arguments
efree(z_args);
// Kick off our request
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
}
/* {{{ proto DUMP key
*/
PHP_METHOD(Redis, dump) {
zval *object;
RedisSock *redis_sock;
char *cmd, *key;
int cmd_len, key_len, key_free;
// Parse our arguments
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
// Grab our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Prefix our key if we need to
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "DUMP", "s", key, key_len);
if(key_free) efree(key);
// Kick off our request
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_ping_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_ping_response);
}
/*
* {{{ proto Redis::restore(ttl, key, value)
*/
PHP_METHOD(Redis, restore) {
zval *object;
RedisSock *redis_sock;
char *cmd, *key, *value;
int cmd_len, key_len, value_len, key_free;
long ttl;
// Parse our arguments
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osls", &object, redis_ce,
&key, &key_len, &ttl, &value, &value_len) == FAILURE) {
RETURN_FALSE;
}
// Grab our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Prefix the key if we need to
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "RESTORE", "sls", key, key_len, ttl, value, value_len);
if(key_free) efree(key);
// Kick off our restore request
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
/*
* {{{ proto Redis::migrate(host port key dest-db timeout)
*/
PHP_METHOD(Redis, migrate) {
zval *object;
RedisSock *redis_sock;
char *cmd, *host, *key;
int cmd_len, host_len, key_len, key_free;
long port, dest_db, timeout;
// Parse arguments
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll", &object, redis_ce,
&host, &host_len, &port, &key, &key_len, &dest_db, &timeout) == FAILURE) {
RETURN_FALSE;
}
// Grabg our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Prefix our key if we need to, build our command
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port, key, key_len, dest_db, timeout);
if(key_free) efree(key);
// Kick off our MIGRATE request
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
/*
* {{{ proto Redis::_prefix(key)
*/
PHP_METHOD(Redis, _prefix) {
zval *object;
RedisSock *redis_sock;
char *key;
int key_len;
// Parse our arguments
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_ce,
&key, &key_len) == FAILURE) {
RETURN_FALSE;
}
// Grab socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Prefix our key if we need to
if(redis_sock->prefix != NULL && redis_sock->prefix_len > 0) {
redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
RETURN_STRINGL(key, key_len, 0);
} else {
RETURN_STRINGL(key, key_len, 1);
}
}
/*
* {{{ proto Redis::_unserialize(value)
*/
PHP_METHOD(Redis, _unserialize) {
zval *object;
RedisSock *redis_sock;
char *value;
int value_len;
// Parse our arguments
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_ce,
&value, &value_len) == FAILURE) {
RETURN_FALSE;
}
// Grab socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// We only need to attempt unserialization if we have a serializer running
if(redis_sock->serializer != REDIS_SERIALIZER_NONE) {
zval *z_ret = NULL;
if(redis_unserialize(redis_sock, value, value_len, &z_ret TSRMLS_CC) == 0) {
// Badly formed input, throw an execption
zend_throw_exception(redis_exception_ce, "Invalid serialized data, or unserialization error", 0 TSRMLS_CC);
RETURN_FALSE;
}
RETURN_ZVAL(z_ret, 0, 1);
} else {
// Just return the value that was passed to us
RETURN_STRINGL(value, value_len, 1);
}
}
/*
* {{{ proto Redis::getLastError()
*/
PHP_METHOD(Redis, getLastError) {
zval *object;
RedisSock *redis_sock;
// Grab our object
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
// Grab socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Return our last error or NULL if we don't have one
if(redis_sock->err != NULL && redis_sock->err_len > 0) {
RETURN_STRINGL(redis_sock->err, redis_sock->err_len, 1);
} else {
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() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
// Grab socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Clear error message
if(redis_sock->err) {
efree(redis_sock->err);
}
redis_sock->err = NULL;
RETURN_TRUE;
}
/*
* {{{ proto Redis::time()
*/
PHP_METHOD(Redis, time) {
zval *object;
RedisSock *redis_sock;
char *cmd;
int cmd_len;
// Grab our object
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) {
RETURN_FALSE;
}
// Grab socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Build TIME command
cmd_len = redis_cmd_format_static(&cmd, "TIME", "");
// Execute or queue command
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
if(redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw);
}
/*
* Introspection stuff
*/
/*
* {{{ proto Redis::IsConnected
*/
PHP_METHOD(Redis, isConnected) {
RedisSock *redis_sock;
if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
}
/*
* {{{ proto Redis::getHost()
*/
PHP_METHOD(Redis, getHost) {
RedisSock *redis_sock;
if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
RETURN_STRING(redis_sock->host, 1);
} 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;
}
}
/*
* {{{ 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;
}
}
/*
* {{{ 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))) {
if(redis_sock->persistent_id != NULL) {
RETURN_STRING(redis_sock->persistent_id, 1);
} else {
RETURN_NULL();
}
} else {
RETURN_FALSE;
}
}
/*
* {{{ proto Redis::getAuth
*/
PHP_METHOD(Redis, getAuth) {
RedisSock *redis_sock;
if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
if(redis_sock->auth != NULL) {
RETURN_STRING(redis_sock->auth, 1);
} else {
RETURN_NULL();
}
} else {
RETURN_FALSE;
}
}
/*
* $redis->client('list');
* $redis->client('kill', <ip:port>);
* $redis->client('setname', <name>);
* $redis->client('getname');
*/
PHP_METHOD(Redis, client) {
zval *object;
RedisSock *redis_sock;
char *cmd, *opt=NULL, *arg=NULL;
int cmd_len, opt_len, arg_len;
// Parse our method parameters
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s",
&object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE)
{
RETURN_FALSE;
}
// Grab our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Build our CLIENT command
if(ZEND_NUM_ARGS() == 2) {
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len,
arg, arg_len);
} else {
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len);
}
// Execute our queue command
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
// We handle CLIENT LIST with a custom response function
if(!strncasecmp(opt, "list", 4)) {
IF_ATOMIC() {
redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL);
}
REDIS_PROCESS_RESPONSE(redis_client_list_reply);
} else {
IF_ATOMIC() {
redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL);
}
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
}
}
/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */