mirror of
https://github.com/php-win-ext/phpredis.git
synced 2026-03-25 01:22:10 +01:00
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
6633 lines
196 KiB
C
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: */
|