mirror of
https://github.com/php-win-ext/phpredis.git
synced 2026-04-28 11:13:16 +02:00
cd05a3441f
Presently PhpRedis will segfault if users attempt to clone either the Redis or RedisCluster objects. We plan on investigating proper clone support in future releases, but for now just diable the handlers.
3837 lines
118 KiB
C
3837 lines
118 KiB
C
/* -*- Mode: C; tab-width: 4 -*- */
|
|
/*
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 1997-2009 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Original author: Alfonso Jimenez <yo@alfonsojimenez.com> |
|
|
| Maintainer: Nicolas Favre-Felix <n.favre-felix@owlient.eu> |
|
|
| Maintainer: Nasreddine Bouafif <n.bouafif@owlient.eu> |
|
|
| Maintainer: Michael Grunder <michael.grunder@gmail.com> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "php_redis.h"
|
|
#include "redis_array.h"
|
|
#include "redis_cluster.h"
|
|
#include "redis_commands.h"
|
|
#include "redis_sentinel.h"
|
|
#include <standard/php_random.h>
|
|
#include <zend_exceptions.h>
|
|
#include <ext/standard/info.h>
|
|
#include <ext/hash/php_hash.h>
|
|
|
|
|
|
#ifdef PHP_SESSION
|
|
#include <ext/session/php_session.h>
|
|
#endif
|
|
|
|
#include "library.h"
|
|
|
|
#ifdef HAVE_REDIS_ZSTD
|
|
#include <zstd.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_REDIS_LZ4
|
|
#include <lz4.h>
|
|
#endif
|
|
|
|
#ifdef PHP_SESSION
|
|
extern ps_module ps_mod_redis;
|
|
extern ps_module ps_mod_redis_cluster;
|
|
#endif
|
|
|
|
extern zend_class_entry *redis_array_ce;
|
|
extern zend_class_entry *redis_cluster_ce;
|
|
extern zend_class_entry *redis_cluster_exception_ce;
|
|
extern zend_class_entry *redis_sentinel_ce;
|
|
|
|
zend_class_entry *redis_ce;
|
|
zend_class_entry *redis_exception_ce;
|
|
|
|
extern int le_cluster_slot_cache;
|
|
|
|
extern zend_function_entry redis_array_functions[];
|
|
extern zend_function_entry redis_cluster_functions[];
|
|
extern zend_function_entry redis_sentinel_functions[];
|
|
|
|
int le_redis_pconnect;
|
|
|
|
PHP_INI_BEGIN()
|
|
/* redis arrays */
|
|
PHP_INI_ENTRY("redis.arrays.algorithm", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.auth", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.autorehash", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.connecttimeout", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.distributor", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.functions", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.hosts", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.index", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.lazyconnect", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.names", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.pconnect", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.readtimeout", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.retryinterval", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.arrays.consistent", "0", PHP_INI_ALL, NULL)
|
|
|
|
/* redis cluster */
|
|
PHP_INI_ENTRY("redis.clusters.cache_slots", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.clusters.auth", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.clusters.persistent", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.clusters.read_timeout", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.clusters.timeout", "0", PHP_INI_ALL, NULL)
|
|
|
|
/* redis pconnect */
|
|
PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "1", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.pconnect.echo_check_liveness", "1", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.pconnect.pool_pattern", "", PHP_INI_ALL, NULL)
|
|
|
|
/* redis session */
|
|
PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.lock_retries", "10", PHP_INI_ALL, NULL)
|
|
PHP_INI_ENTRY("redis.session.lock_wait_time", "2000", PHP_INI_ALL, NULL)
|
|
PHP_INI_END()
|
|
|
|
/** {{{ Argument info for commands in redis 1.0 */
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_connect, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, host)
|
|
ZEND_ARG_INFO(0, port)
|
|
ZEND_ARG_INFO(0, timeout)
|
|
ZEND_ARG_INFO(0, retry_interval)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, option)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_multi, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, mode)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_client, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, cmd)
|
|
ZEND_ARG_VARIADIC_INFO(0, args)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2)
|
|
ZEND_ARG_INFO(0, cmd)
|
|
ZEND_ARG_INFO(0, key)
|
|
ZEND_ARG_INFO(0, value)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, async)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, cmd)
|
|
ZEND_ARG_VARIADIC_INFO(0, args)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, arg)
|
|
ZEND_ARG_INFO(0, option)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_pconnect, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, host)
|
|
ZEND_ARG_INFO(0, port)
|
|
ZEND_ARG_INFO(0, timeout)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1)
|
|
ZEND_ARG_ARRAY_INFO(0, keys, 0)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_exists, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, key)
|
|
ZEND_ARG_VARIADIC_INFO(0, other_keys)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, key)
|
|
ZEND_ARG_VARIADIC_INFO(0, other_keys)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, pattern)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_generic_sort, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, key)
|
|
ZEND_ARG_INFO(0, pattern)
|
|
ZEND_ARG_INFO(0, get)
|
|
ZEND_ARG_INFO(0, start)
|
|
ZEND_ARG_INFO(0, end)
|
|
ZEND_ARG_INFO(0, getList)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_lrem, 0, 0, 3)
|
|
ZEND_ARG_INFO(0, key)
|
|
ZEND_ARG_INFO(0, value)
|
|
ZEND_ARG_INFO(0, count)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_auth, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, auth)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_select, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, dbindex)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_move, 0, 0, 2)
|
|
ZEND_ARG_INFO(0, key)
|
|
ZEND_ARG_INFO(0, dbindex)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_slaveof, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, host)
|
|
ZEND_ARG_INFO(0, port)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_acl, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, subcmd)
|
|
ZEND_ARG_VARIADIC_INFO(0, args)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
/* }}} */
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_migrate, 0, 0, 5)
|
|
ZEND_ARG_INFO(0, host)
|
|
ZEND_ARG_INFO(0, port)
|
|
ZEND_ARG_INFO(0, key)
|
|
ZEND_ARG_INFO(0, db)
|
|
ZEND_ARG_INFO(0, timeout)
|
|
ZEND_ARG_INFO(0, copy)
|
|
ZEND_ARG_INFO(0, replace)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_wait, 0, 0, 2)
|
|
ZEND_ARG_INFO(0, numslaves)
|
|
ZEND_ARG_INFO(0, timeout)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, cmd)
|
|
ZEND_ARG_VARIADIC_INFO(0, args)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
/**
|
|
* Argument info for the SCAN proper
|
|
*/
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_scan, 0, 0, 1)
|
|
ZEND_ARG_INFO(1, i_iterator)
|
|
ZEND_ARG_INFO(0, str_pattern)
|
|
ZEND_ARG_INFO(0, i_count)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
/**
|
|
* Argument info for key scanning
|
|
*/
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2)
|
|
ZEND_ARG_INFO(0, str_key)
|
|
ZEND_ARG_INFO(1, i_iterator)
|
|
ZEND_ARG_INFO(0, str_pattern)
|
|
ZEND_ARG_INFO(0, i_count)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
static zend_function_entry redis_functions[] = {
|
|
PHP_ME(Redis, __construct, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, __destruct, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, _prefix, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, _serialize, arginfo_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, _unserialize, arginfo_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, acl, arginfo_acl, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, append, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, auth, arginfo_auth, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, bgSave, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, bitcount, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, bitop, arginfo_bitop, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, blPop, arginfo_blrpop, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, brPop, arginfo_blrpop, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, bzPopMax, arginfo_blrpop, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, bzPopMin, arginfo_blrpop, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, clearLastError, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, client, arginfo_client, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, close, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, command, arginfo_command, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, config, arginfo_config, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, connect, arginfo_connect, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, dbSize, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, debug, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, decr, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, decrBy, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, del, arginfo_del, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, discard, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, dump, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, echo, arginfo_echo, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, eval, arginfo_eval, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, exec, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, exists, arginfo_exists, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, expire, arginfo_expire, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, expireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, flushAll, arginfo_flush, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, flushDB, arginfo_flush, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, geodist, arginfo_geodist, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, geohash, arginfo_key_members, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, geopos, arginfo_key_members, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, georadius, arginfo_georadius, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, georadius_ro, arginfo_georadius, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, georadiusbymember_ro, arginfo_georadiusbymember, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, get, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getAuth, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getBit, arginfo_key_offset, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getDBNum, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getHost, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getLastError, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getMode, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getOption, arginfo_getoption, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getPersistentID, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getPort, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getReadTimeout, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getSet, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, getTimeout, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hDel, arginfo_key_members, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hExists, arginfo_key_member, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hGet, arginfo_key_member, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hGetAll, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hIncrBy, arginfo_key_member_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hIncrByFloat, arginfo_key_member_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hKeys, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hLen, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hMget, arginfo_hmget, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hMset, arginfo_hmset, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hSet, arginfo_key_member_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hSetNx, arginfo_key_member_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hStrLen, arginfo_key_member, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hVals, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, incr, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, incrBy, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, incrByFloat, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, info, arginfo_info, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, isConnected, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, keys, arginfo_keys, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, lInsert, arginfo_linsert, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, lLen, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, lPop, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, lPush, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, lPushx, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, lSet, arginfo_lset, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, lastSave, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, lindex, arginfo_lindex, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, lrem, arginfo_lrem, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, mget, arginfo_mget, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, migrate, arginfo_migrate, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, move, arginfo_move, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, mset, arginfo_pairs, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, multi, arginfo_multi, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, object, arginfo_object, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, persist, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, pexpireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, pfcount, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, ping, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, pipeline, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, pttl, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, publish, arginfo_publish, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, pubsub, arginfo_pubsub, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, rPop, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, rPush, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, rPushx, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, randomKey, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, renameNx, arginfo_key_newkey, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, restore, arginfo_restore, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, role, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sAdd, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sAddArray, arginfo_sadd_array, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sDiff, arginfo_nkeys, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sDiffStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sInter, arginfo_nkeys, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sInterStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sMembers, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sMove, arginfo_smove, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sPop, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sRandMember, arginfo_srand_member, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sUnion, arginfo_nkeys, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sUnionStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, save, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, scard, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, script, arginfo_script, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, select, arginfo_select, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, set, arginfo_set, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, setBit, arginfo_key_offset_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, setOption, arginfo_setoption, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, setRange, arginfo_key_offset_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, setnx, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sismember, arginfo_key_value, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, slaveof, arginfo_slaveof, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sort, arginfo_sort, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sortAsc, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_ME(Redis, sortAscAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_ME(Redis, sortDesc, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_ME(Redis, sortDescAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_ME(Redis, srem, arginfo_key_members, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, strlen, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, swapdb, arginfo_swapdb, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, type, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, unlink, arginfo_nkeys, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, wait, arginfo_wait, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, watch, arginfo_watch, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xack, arginfo_xack, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xadd, arginfo_xadd, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xclaim, arginfo_xclaim, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xdel, arginfo_xdel, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xgroup, arginfo_xgroup, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xinfo, arginfo_xinfo, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xlen, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xpending, arginfo_xpending, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xrange, arginfo_xrange, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xread, arginfo_xread, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xreadgroup, arginfo_xreadgroup, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xrevrange, arginfo_xrange, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, xtrim, arginfo_xtrim, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zAdd, arginfo_zadd, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zCard, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zCount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zIncrBy, arginfo_zincrby, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zLexCount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zPopMax, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zPopMin, arginfo_key, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRange, arginfo_zrange, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRank, arginfo_key_member, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRem, arginfo_key_members, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRemRangeByLex, arginfo_key_min_max, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRemRangeByRank, arginfo_key_start_end, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRevRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRevRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zRevRank, arginfo_key_member, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
|
|
PHP_ME(Redis, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC)
|
|
|
|
/* Mark all of these aliases deprecated. They aren't actual Redis commands. */
|
|
PHP_MALIAS(Redis, delete, del, arginfo_del, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, evaluate, eval, arginfo_eval, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, evaluateSha, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, getKeys, keys, arginfo_keys, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, lGet, lindex, arginfo_lindex, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, lGetRange, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, lRemove, lrem, arginfo_lrem, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, lSize, lLen, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, listTrim, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, open, connect, arginfo_connect, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, popen, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, renameKey, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, sContains, sismember, arginfo_key_value, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, sGetMembers, sMembers, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, sRemove, srem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, sSize, scard, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, setTimeout, expire, arginfo_expire, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, zDelete, zRem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, zDeleteRangeByRank, zRemRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, zDeleteRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, zInter, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, zRemove, zRem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, zRemoveRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, zReverseRange, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, zSize, zCard, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_MALIAS(Redis, zUnion, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
|
|
PHP_FE_END
|
|
};
|
|
|
|
static const zend_module_dep redis_deps[] = {
|
|
#ifdef HAVE_REDIS_IGBINARY
|
|
ZEND_MOD_REQUIRED("igbinary")
|
|
#endif
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
ZEND_MOD_REQUIRED("msgpack")
|
|
#endif
|
|
#ifdef HAVE_REDIS_JSON
|
|
ZEND_MOD_REQUIRED("json")
|
|
#endif
|
|
#ifdef PHP_SESSION
|
|
ZEND_MOD_REQUIRED("session")
|
|
#endif
|
|
ZEND_MOD_END
|
|
};
|
|
|
|
ZEND_DECLARE_MODULE_GLOBALS(redis)
|
|
static PHP_GINIT_FUNCTION(redis);
|
|
|
|
zend_module_entry redis_module_entry = {
|
|
STANDARD_MODULE_HEADER_EX,
|
|
NULL,
|
|
redis_deps,
|
|
"redis",
|
|
NULL,
|
|
PHP_MINIT(redis),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
PHP_MINFO(redis),
|
|
PHP_REDIS_VERSION,
|
|
PHP_MODULE_GLOBALS(redis),
|
|
PHP_GINIT(redis),
|
|
NULL,
|
|
NULL,
|
|
STANDARD_MODULE_PROPERTIES_EX
|
|
};
|
|
|
|
#ifdef COMPILE_DL_REDIS
|
|
ZEND_GET_MODULE(redis)
|
|
#endif
|
|
|
|
zend_object_handlers redis_object_handlers;
|
|
|
|
/* Send a static DISCARD in case we're in MULTI mode. */
|
|
static int
|
|
redis_send_discard(RedisSock *redis_sock)
|
|
{
|
|
int result = FAILURE;
|
|
char *cmd, *resp;
|
|
int resp_len, cmd_len;
|
|
|
|
/* format our discard command */
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "DISCARD", "");
|
|
|
|
/* send our DISCARD command */
|
|
if (redis_sock_write(redis_sock, cmd, cmd_len) >= 0 &&
|
|
(resp = redis_sock_read(redis_sock,&resp_len)) != NULL)
|
|
{
|
|
/* success if we get OK */
|
|
result = (resp_len == 3 && strncmp(resp,"+OK", 3) == 0) ? SUCCESS:FAILURE;
|
|
|
|
/* free our response */
|
|
efree(resp);
|
|
}
|
|
|
|
/* free our command */
|
|
efree(cmd);
|
|
|
|
/* return success/failure */
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
free_reply_callbacks(RedisSock *redis_sock)
|
|
{
|
|
fold_item *fi;
|
|
|
|
for (fi = redis_sock->head; fi; ) {
|
|
fold_item *fi_next = fi->next;
|
|
free(fi);
|
|
fi = fi_next;
|
|
}
|
|
redis_sock->head = NULL;
|
|
redis_sock->current = NULL;
|
|
}
|
|
|
|
/* Passthru for destroying cluster cache */
|
|
static void cluster_cache_dtor(zend_resource *rsrc) {
|
|
if (rsrc->ptr) {
|
|
cluster_cache_free(rsrc->ptr);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_redis_object(zend_object *object)
|
|
{
|
|
redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object);
|
|
|
|
zend_object_std_dtor(&redis->std);
|
|
if (redis->sock) {
|
|
redis_sock_disconnect(redis->sock, 0);
|
|
redis_free_socket(redis->sock);
|
|
}
|
|
}
|
|
|
|
zend_object *
|
|
create_redis_object(zend_class_entry *ce)
|
|
{
|
|
redis_object *redis = ecalloc(1, sizeof(redis_object) + zend_object_properties_size(ce));
|
|
|
|
redis->sock = NULL;
|
|
|
|
zend_object_std_init(&redis->std, ce);
|
|
object_properties_init(&redis->std, ce);
|
|
|
|
memcpy(&redis_object_handlers, zend_get_std_object_handlers(), sizeof(redis_object_handlers));
|
|
redis_object_handlers.offset = XtOffsetOf(redis_object, std);
|
|
redis_object_handlers.free_obj = free_redis_object;
|
|
redis_object_handlers.clone_obj = NULL;
|
|
redis->std.handlers = &redis_object_handlers;
|
|
|
|
return &redis->std;
|
|
}
|
|
|
|
static zend_always_inline RedisSock *
|
|
redis_sock_get_instance(zval *id, int no_throw)
|
|
{
|
|
redis_object *redis;
|
|
|
|
if (Z_TYPE_P(id) == IS_OBJECT) {
|
|
redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, id);
|
|
if (redis->sock) {
|
|
return redis->sock;
|
|
}
|
|
}
|
|
// Throw an exception unless we've been requested not to
|
|
if (!no_throw) {
|
|
REDIS_THROW_EXCEPTION("Redis server went away", 0);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* redis_sock_get
|
|
*/
|
|
PHP_REDIS_API RedisSock *
|
|
redis_sock_get(zval *id, int no_throw)
|
|
{
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_instance(id, no_throw)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (redis_sock_server_open(redis_sock) < 0) {
|
|
if (!no_throw) {
|
|
char *errmsg = NULL;
|
|
if (redis_sock->port < 0) {
|
|
spprintf(&errmsg, 0, "Redis server %s went away", ZSTR_VAL(redis_sock->host));
|
|
} else {
|
|
spprintf(&errmsg, 0, "Redis server %s:%d went away", ZSTR_VAL(redis_sock->host), redis_sock->port);
|
|
}
|
|
REDIS_THROW_EXCEPTION(errmsg, 0);
|
|
efree(errmsg);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
return redis_sock;
|
|
}
|
|
|
|
/**
|
|
* redis_sock_get_direct
|
|
* Returns our attached RedisSock pointer if we're connected
|
|
*/
|
|
PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
|
|
// If we can't grab our object, or get a socket, or we're not connected,
|
|
// return NULL
|
|
if((zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
|
|
&object, redis_ce) == FAILURE) ||
|
|
(redis_sock = redis_sock_get(object, 1)) == NULL ||
|
|
redis_sock->status < REDIS_SOCK_STATUS_CONNECTED)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Return our socket */
|
|
return redis_sock;
|
|
}
|
|
|
|
/* Redis and RedisCluster objects share serialization/prefixing settings so
|
|
* this is a generic function to add class constants to either */
|
|
static void add_class_constants(zend_class_entry *ce, int is_cluster) {
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_NOT_FOUND"), REDIS_NOT_FOUND);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STRING"), REDIS_STRING);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_SET"), REDIS_SET);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_LIST"), REDIS_LIST);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_ZSET"), REDIS_ZSET);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_HASH"), REDIS_HASH);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STREAM"), REDIS_STREAM);
|
|
|
|
/* Cluster doesn't support pipelining at this time */
|
|
if(!is_cluster) {
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("PIPELINE"), PIPELINE);
|
|
}
|
|
|
|
/* Add common mode constants */
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("ATOMIC"), ATOMIC);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("MULTI"), MULTI);
|
|
|
|
/* options */
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SERIALIZER"), REDIS_OPT_SERIALIZER);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_PREFIX"), REDIS_OPT_PREFIX);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_TCP_KEEPALIVE"), REDIS_OPT_TCP_KEEPALIVE);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION"), REDIS_OPT_COMPRESSION);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_REPLY_LITERAL"), REDIS_OPT_REPLY_LITERAL);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION_LEVEL"), REDIS_OPT_COMPRESSION_LEVEL);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_NULL_MULTIBULK_AS_NULL"), REDIS_OPT_NULL_MBULK_AS_NULL);
|
|
|
|
/* serializer */
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_PHP"), REDIS_SERIALIZER_PHP);
|
|
#ifdef HAVE_REDIS_IGBINARY
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY);
|
|
#endif
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_MSGPACK"), REDIS_SERIALIZER_MSGPACK);
|
|
#endif
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_JSON"), REDIS_SERIALIZER_JSON);
|
|
|
|
/* compression */
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_NONE"), REDIS_COMPRESSION_NONE);
|
|
#ifdef HAVE_REDIS_LZF
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZF"), REDIS_COMPRESSION_LZF);
|
|
#endif
|
|
#ifdef HAVE_REDIS_ZSTD
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD"), REDIS_COMPRESSION_ZSTD);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_MIN"), 1);
|
|
#ifdef ZSTD_CLEVEL_DEFAULT
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_DEFAULT"), ZSTD_CLEVEL_DEFAULT);
|
|
#else
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_DEFAULT"), 3);
|
|
#endif
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_MAX"), ZSTD_maxCLevel());
|
|
#endif
|
|
|
|
#ifdef HAVE_REDIS_LZ4
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZ4"), REDIS_COMPRESSION_LZ4);
|
|
#endif
|
|
|
|
/* scan options*/
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NORETRY"), REDIS_SCAN_NORETRY);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_PREFIX"), REDIS_SCAN_PREFIX);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NOPREFIX"), REDIS_SCAN_NOPREFIX);
|
|
|
|
/* Cluster option to allow for slave failover */
|
|
if (is_cluster) {
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SLAVE_FAILOVER"), REDIS_OPT_FAILOVER);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_NONE"), REDIS_FAILOVER_NONE);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_ERROR"), REDIS_FAILOVER_ERROR);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE"), REDIS_FAILOVER_DISTRIBUTE);
|
|
zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE_SLAVES"), REDIS_FAILOVER_DISTRIBUTE_SLAVES);
|
|
}
|
|
|
|
zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5);
|
|
zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6);
|
|
}
|
|
|
|
static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor)
|
|
{
|
|
if (res->ptr) {
|
|
ConnectionPool *p = res->ptr;
|
|
zend_llist_destroy(&p->list);
|
|
pefree(res->ptr, 1);
|
|
}
|
|
}
|
|
|
|
static void redis_random_hex_bytes(char *dst, size_t dstsize) {
|
|
char chunk[9], *ptr = dst;
|
|
ssize_t rem = dstsize, len, clen;
|
|
size_t bytes;
|
|
|
|
/* We need two characters per hex byte */
|
|
bytes = dstsize / 2;
|
|
zend_string *s = zend_string_alloc(bytes, 0);
|
|
|
|
/* First try to have PHP generate the bytes */
|
|
if (php_random_bytes_silent(ZSTR_VAL(s), bytes) == SUCCESS) {
|
|
php_hash_bin2hex(dst, (unsigned char *)ZSTR_VAL(s), bytes);
|
|
zend_string_release(s);
|
|
return;
|
|
}
|
|
|
|
/* PHP shouldn't have failed, but generate manually if it did. */
|
|
while (rem > 0) {
|
|
clen = snprintf(chunk, sizeof(chunk), "%08x", rand());
|
|
len = rem >= clen ? clen : rem;
|
|
memcpy(ptr, chunk, len);
|
|
ptr += len; rem -= len;
|
|
}
|
|
|
|
zend_string_release(s);
|
|
}
|
|
|
|
static PHP_GINIT_FUNCTION(redis)
|
|
{
|
|
redis_random_hex_bytes(redis_globals->salt, sizeof(redis_globals->salt) - 1);
|
|
redis_globals->salt[sizeof(redis_globals->salt)-1] = '\0';
|
|
}
|
|
|
|
/**
|
|
* PHP_MINIT_FUNCTION
|
|
*/
|
|
PHP_MINIT_FUNCTION(redis)
|
|
{
|
|
struct timeval tv;
|
|
|
|
zend_class_entry redis_class_entry;
|
|
zend_class_entry redis_array_class_entry;
|
|
zend_class_entry redis_cluster_class_entry;
|
|
zend_class_entry redis_sentinel_class_entry;
|
|
zend_class_entry redis_exception_class_entry;
|
|
zend_class_entry redis_cluster_exception_class_entry;
|
|
|
|
zend_class_entry *exception_ce = NULL;
|
|
|
|
/* Seed random generator (for RedisCluster failover) */
|
|
gettimeofday(&tv, NULL);
|
|
srand(tv.tv_usec * tv.tv_sec);
|
|
|
|
REGISTER_INI_ENTRIES();
|
|
|
|
/* Redis class */
|
|
INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions);
|
|
redis_ce = zend_register_internal_class(&redis_class_entry);
|
|
redis_ce->create_object = create_redis_object;
|
|
|
|
/* RedisArray class */
|
|
INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions);
|
|
redis_array_ce = zend_register_internal_class(&redis_array_class_entry);
|
|
redis_array_ce->create_object = create_redis_array_object;
|
|
|
|
/* RedisCluster class */
|
|
INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", redis_cluster_functions);
|
|
redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry);
|
|
redis_cluster_ce->create_object = create_cluster_context;
|
|
|
|
/* RedisSentinel class */
|
|
INIT_CLASS_ENTRY(redis_sentinel_class_entry, "RedisSentinel", redis_sentinel_functions);
|
|
redis_sentinel_ce = zend_register_internal_class(&redis_sentinel_class_entry);
|
|
redis_sentinel_ce->create_object = create_sentinel_object;
|
|
|
|
/* Register our cluster cache list item */
|
|
le_cluster_slot_cache = zend_register_list_destructors_ex(NULL, cluster_cache_dtor,
|
|
"Redis cluster slot cache",
|
|
module_number);
|
|
|
|
/* Base Exception class */
|
|
exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1);
|
|
if (exception_ce == NULL) {
|
|
exception_ce = zend_exception_get_default();
|
|
}
|
|
|
|
/* RedisException class */
|
|
INIT_CLASS_ENTRY(redis_exception_class_entry, "RedisException", NULL);
|
|
redis_exception_ce = zend_register_internal_class_ex(
|
|
&redis_exception_class_entry,
|
|
exception_ce);
|
|
|
|
/* RedisClusterException class */
|
|
INIT_CLASS_ENTRY(redis_cluster_exception_class_entry,
|
|
"RedisClusterException", NULL);
|
|
redis_cluster_exception_ce = zend_register_internal_class_ex(
|
|
&redis_cluster_exception_class_entry, exception_ce);
|
|
|
|
/* Add shared class constants to Redis and RedisCluster objects */
|
|
add_class_constants(redis_ce, 0);
|
|
add_class_constants(redis_cluster_ce, 1);
|
|
|
|
#ifdef PHP_SESSION
|
|
php_session_register_module(&ps_mod_redis);
|
|
php_session_register_module(&ps_mod_redis_cluster);
|
|
#endif
|
|
|
|
/* Register resource destructors */
|
|
le_redis_pconnect = zend_register_list_destructors_ex(NULL, redis_connections_pool_dtor,
|
|
"phpredis persistent connections pool", module_number);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
static const char *
|
|
get_available_serializers(void)
|
|
{
|
|
#ifdef HAVE_REDIS_JSON
|
|
#ifdef HAVE_REDIS_IGBINARY
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
return "php, json, igbinary, msgpack";
|
|
#else
|
|
return "php, json, igbinary";
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
return "php, json, msgpack";
|
|
#else
|
|
return "php, json";
|
|
#endif
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_REDIS_IGBINARY
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
return "php, igbinary, msgpack";
|
|
#else
|
|
return "php, igbinary";
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_REDIS_MSGPACK
|
|
return "php, msgpack";
|
|
#else
|
|
return "php";
|
|
#endif
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* PHP_MINFO_FUNCTION
|
|
*/
|
|
PHP_MINFO_FUNCTION(redis)
|
|
{
|
|
smart_str names = {0,};
|
|
|
|
php_info_print_table_start();
|
|
php_info_print_table_header(2, "Redis Support", "enabled");
|
|
php_info_print_table_row(2, "Redis Version", PHP_REDIS_VERSION);
|
|
php_info_print_table_row(2, "Redis Sentinel Version", PHP_REDIS_SENTINEL_VERSION);
|
|
#ifdef GIT_REVISION
|
|
php_info_print_table_row(2, "Git revision", "$Id: " GIT_REVISION " $");
|
|
#endif
|
|
php_info_print_table_row(2, "Available serializers", get_available_serializers());
|
|
#ifdef HAVE_REDIS_LZF
|
|
smart_str_appends(&names, "lzf");
|
|
#endif
|
|
#ifdef HAVE_REDIS_ZSTD
|
|
if (names.s) {
|
|
smart_str_appends(&names, ", ");
|
|
}
|
|
smart_str_appends(&names, "zstd");
|
|
#endif
|
|
#ifdef HAVE_REDIS_LZ4
|
|
if (names.s) {
|
|
smart_str_appends(&names, ", ");
|
|
}
|
|
smart_str_appends(&names, "lz4");
|
|
#endif
|
|
if (names.s) {
|
|
smart_str_0(&names);
|
|
php_info_print_table_row(2, "Available compression", ZSTR_VAL(names.s));
|
|
}
|
|
smart_str_free(&names);
|
|
php_info_print_table_end();
|
|
|
|
DISPLAY_INI_ENTRIES();
|
|
}
|
|
|
|
/* {{{ proto Redis Redis::__construct()
|
|
Public constructor */
|
|
PHP_METHOD(Redis, __construct)
|
|
{
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis Redis::__destruct()
|
|
Public Destructor
|
|
*/
|
|
PHP_METHOD(Redis,__destruct) {
|
|
if(zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
// Grab our socket
|
|
RedisSock *redis_sock;
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 1)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
// If we think we're in MULTI mode, send a discard
|
|
if (IS_MULTI(redis_sock)) {
|
|
if (!IS_PIPELINE(redis_sock) && redis_sock->stream) {
|
|
// Discard any multi commands, and free any callbacks that have been
|
|
// queued
|
|
redis_send_discard(redis_sock);
|
|
}
|
|
free_reply_callbacks(redis_sock);
|
|
}
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::connect(string host, int port [, double timeout [, long retry_interval]])
|
|
*/
|
|
PHP_METHOD(Redis, connect)
|
|
{
|
|
if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0) == FAILURE) {
|
|
RETURN_FALSE;
|
|
} else {
|
|
RETURN_TRUE;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::pconnect(string host, int port [, double timeout])
|
|
*/
|
|
PHP_METHOD(Redis, pconnect)
|
|
{
|
|
if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1) == FAILURE) {
|
|
RETURN_FALSE;
|
|
} else {
|
|
RETURN_TRUE;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_REDIS_API int
|
|
redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
|
|
{
|
|
zval *object, *context = NULL, *ele;
|
|
char *host = NULL, *persistent_id = NULL;
|
|
zend_long port = -1, retry_interval = 0;
|
|
size_t host_len, persistent_id_len;
|
|
double timeout = 0.0, read_timeout = 0.0;
|
|
redis_object *redis;
|
|
|
|
#ifdef ZTS
|
|
/* not sure how in threaded mode this works so disabled persistence at
|
|
* first */
|
|
persistent = 0;
|
|
#endif
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"Os|lds!lda", &object, redis_ce, &host,
|
|
&host_len, &port, &timeout, &persistent_id,
|
|
&persistent_id_len, &retry_interval,
|
|
&read_timeout, &context) == FAILURE)
|
|
{
|
|
return FAILURE;
|
|
}
|
|
|
|
/* Disregard persistent_id if we're not opening a persistent connection */
|
|
if (!persistent) {
|
|
persistent_id = NULL;
|
|
}
|
|
|
|
if (timeout < 0L || timeout > INT_MAX) {
|
|
REDIS_THROW_EXCEPTION("Invalid connect timeout", 0);
|
|
return FAILURE;
|
|
}
|
|
|
|
if (read_timeout < 0L || read_timeout > INT_MAX) {
|
|
REDIS_THROW_EXCEPTION("Invalid read timeout", 0);
|
|
return FAILURE;
|
|
}
|
|
|
|
if (retry_interval < 0L || retry_interval > INT_MAX) {
|
|
REDIS_THROW_EXCEPTION("Invalid retry interval", 0);
|
|
return FAILURE;
|
|
}
|
|
|
|
/* If it's not a unix socket, set to default */
|
|
if(port == -1 && host_len && host[0] != '/') {
|
|
port = 6379;
|
|
}
|
|
|
|
redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, object);
|
|
|
|
/* if there is a redis sock already we have to remove it */
|
|
if (redis->sock) {
|
|
redis_sock_disconnect(redis->sock, 0);
|
|
redis_free_socket(redis->sock);
|
|
}
|
|
|
|
redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, persistent,
|
|
persistent_id, retry_interval);
|
|
|
|
if (context) {
|
|
/* Stream context (e.g. TLS) */
|
|
if ((ele = REDIS_HASH_STR_FIND_STATIC(Z_ARRVAL_P(context), "stream"))) {
|
|
redis_sock_set_stream_context(redis->sock, ele);
|
|
}
|
|
|
|
/* AUTH */
|
|
if ((ele = REDIS_HASH_STR_FIND_STATIC(Z_ARRVAL_P(context), "auth"))) {
|
|
redis_sock_set_auth_zval(redis->sock, ele);
|
|
}
|
|
}
|
|
|
|
if (redis_sock_server_open(redis->sock) < 0) {
|
|
if (redis->sock->err) {
|
|
REDIS_THROW_EXCEPTION(ZSTR_VAL(redis->sock->err), 0);
|
|
}
|
|
redis_free_socket(redis->sock);
|
|
redis->sock = NULL;
|
|
return FAILURE;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* {{{ proto long Redis::bitop(string op, string key, ...) */
|
|
PHP_METHOD(Redis, bitop)
|
|
{
|
|
REDIS_PROCESS_CMD(bitop, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::bitcount(string key, [int start], [int end])
|
|
*/
|
|
PHP_METHOD(Redis, bitcount)
|
|
{
|
|
REDIS_PROCESS_CMD(bitcount, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto integer Redis::bitpos(string key, int bit, [int start, int end]) */
|
|
PHP_METHOD(Redis, bitpos)
|
|
{
|
|
REDIS_PROCESS_CMD(bitpos, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::close()
|
|
*/
|
|
PHP_METHOD(Redis, close)
|
|
{
|
|
RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU);
|
|
|
|
if (redis_sock_disconnect(redis_sock, 1) == SUCCESS) {
|
|
RETURN_TRUE;
|
|
}
|
|
RETURN_FALSE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::set(string key, mixed val, long timeout,
|
|
* [array opt) */
|
|
PHP_METHOD(Redis, set) {
|
|
REDIS_PROCESS_CMD(set, redis_boolean_response);
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::setex(string key, long expire, string value)
|
|
*/
|
|
PHP_METHOD(Redis, setex)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, redis_boolean_response);
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::psetex(string key, long expire, string value)
|
|
*/
|
|
PHP_METHOD(Redis, psetex)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, redis_boolean_response);
|
|
}
|
|
|
|
/* {{{ proto boolean Redis::setnx(string key, string value)
|
|
*/
|
|
PHP_METHOD(Redis, setnx)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SETNX", redis_kv_cmd, redis_1_response);
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::getSet(string key, string value)
|
|
*/
|
|
PHP_METHOD(Redis, getSet)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("GETSET", redis_kv_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::randomKey()
|
|
*/
|
|
PHP_METHOD(Redis, randomKey)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RANDOMKEY", redis_empty_cmd, redis_ping_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::echo(string msg)
|
|
*/
|
|
PHP_METHOD(Redis, echo)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ECHO", redis_str_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::rename(string key_src, string key_dst)
|
|
*/
|
|
PHP_METHOD(Redis, rename)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::renameNx(string key_src, string key_dst)
|
|
*/
|
|
PHP_METHOD(Redis, renameNx)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RENAMENX", redis_key_key_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::get(string key)
|
|
*/
|
|
PHP_METHOD(Redis, get)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto string Redis::ping()
|
|
*/
|
|
PHP_METHOD(Redis, ping)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("PING", redis_opt_str_cmd, redis_read_variant_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::incr(string key [,int value])
|
|
*/
|
|
PHP_METHOD(Redis, incr){
|
|
REDIS_PROCESS_CMD(incr, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::incrBy(string key ,int value)
|
|
*/
|
|
PHP_METHOD(Redis, incrBy){
|
|
REDIS_PROCESS_KW_CMD("INCRBY", redis_key_long_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto float Redis::incrByFloat(string key, float value)
|
|
*/
|
|
PHP_METHOD(Redis, incrByFloat) {
|
|
REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd,
|
|
redis_bulk_double_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::decr(string key) */
|
|
PHP_METHOD(Redis, decr)
|
|
{
|
|
REDIS_PROCESS_CMD(decr, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::decrBy(string key ,int value)
|
|
*/
|
|
PHP_METHOD(Redis, decrBy){
|
|
REDIS_PROCESS_KW_CMD("DECRBY", redis_key_long_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::mget(array keys)
|
|
*/
|
|
PHP_METHOD(Redis, mget)
|
|
{
|
|
zval *object, *z_args, *z_ele;
|
|
HashTable *hash;
|
|
RedisSock *redis_sock;
|
|
smart_string cmd = {0};
|
|
int arg_count;
|
|
|
|
/* Make sure we have proper arguments */
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa",
|
|
&object, redis_ce, &z_args) == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* We'll need the socket */
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
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 */
|
|
ZEND_HASH_FOREACH_VAL(hash, z_ele) {
|
|
zend_string *zstr = zval_get_string(z_ele);
|
|
redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, NULL);
|
|
zend_string_release(zstr);
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
/* Kick off our command */
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
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)
|
|
{
|
|
REDIS_PROCESS_CMD(exists, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::del(string key)
|
|
*/
|
|
PHP_METHOD(Redis, del)
|
|
{
|
|
REDIS_PROCESS_CMD(del, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::unlink(string $key1, string $key2 [, string $key3...]) }}}
|
|
* {{{ proto long Redis::unlink(array $keys) */
|
|
PHP_METHOD(Redis, unlink)
|
|
{
|
|
REDIS_PROCESS_CMD(unlink, redis_long_response);
|
|
}
|
|
|
|
PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock)
|
|
{
|
|
redis_sock->watching = 1;
|
|
}
|
|
|
|
PHP_REDIS_API 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)
|
|
{
|
|
REDIS_PROCESS_CMD(watch, redis_watch_response);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock)
|
|
{
|
|
redis_sock->watching = 0;
|
|
}
|
|
|
|
PHP_REDIS_API 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)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("UNWATCH", redis_empty_cmd, redis_unwatch_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::keys(string pattern)
|
|
*/
|
|
PHP_METHOD(Redis, keys)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("KEYS", redis_key_cmd, redis_mbulk_reply_raw);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int Redis::type(string key)
|
|
*/
|
|
PHP_METHOD(Redis, type)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("TYPE", redis_key_cmd, redis_type_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto mixed Redis::acl(string $op, ...) }}} */
|
|
PHP_METHOD(Redis, acl) {
|
|
RedisSock *redis_sock;
|
|
FailableResultCallback cb;
|
|
zval *zargs;
|
|
zend_string *op;
|
|
char *cmd;
|
|
int cmdlen, argc = ZEND_NUM_ARGS();
|
|
|
|
if (argc < 1 || (redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
|
|
if (argc < 1) {
|
|
php_error_docref(NULL, E_WARNING, "ACL command requires at least one argument");
|
|
}
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
zargs = emalloc(argc * sizeof(*zargs));
|
|
if (zend_get_parameters_array(ht, argc, zargs) == FAILURE) {
|
|
efree(zargs);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Read the subcommand and set response callback */
|
|
op = zval_get_string(&zargs[0]);
|
|
if (zend_string_equals_literal_ci(op, "GETUSER")) {
|
|
cb = redis_acl_getuser_reply;
|
|
} else if (zend_string_equals_literal_ci(op, "LOG")) {
|
|
cb = redis_acl_log_reply;
|
|
} else {
|
|
cb = redis_read_variant_reply;
|
|
}
|
|
|
|
/* Make our command and free args */
|
|
cmd = redis_variadic_str_cmd("ACL", zargs, argc, &cmdlen);
|
|
|
|
zend_string_release(op);
|
|
efree(zargs);
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmdlen);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
if (cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
REDIS_PROCESS_RESPONSE(cb);
|
|
}
|
|
|
|
/* {{{ proto long Redis::append(string key, string val) */
|
|
PHP_METHOD(Redis, append)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("APPEND", redis_kv_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::GetRange(string key, long start, long end) */
|
|
PHP_METHOD(Redis, getRange)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd,
|
|
redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::setRange(string key, long start, string value) */
|
|
PHP_METHOD(Redis, setRange)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd,
|
|
redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::getbit(string key, long idx) */
|
|
PHP_METHOD(Redis, getBit)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("GETBIT", redis_key_long_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::setbit(string key, long idx, bool|int value) */
|
|
PHP_METHOD(Redis, setBit)
|
|
{
|
|
REDIS_PROCESS_CMD(setbit, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::strlen(string key) */
|
|
PHP_METHOD(Redis, strlen)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("STRLEN", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::lPush(string key , string value)
|
|
*/
|
|
PHP_METHOD(Redis, lPush)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LPUSH", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::rPush(string key , string value)
|
|
*/
|
|
PHP_METHOD(Redis, rPush)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RPUSH", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, lInsert)
|
|
{
|
|
REDIS_PROCESS_CMD(linsert, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto long Redis::lPushx(string key, mixed value) */
|
|
PHP_METHOD(Redis, lPushx)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::rPushx(string key, mixed value) */
|
|
PHP_METHOD(Redis, rPushx)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::lPOP(string key) */
|
|
PHP_METHOD(Redis, lPop)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LPOP", redis_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::rPOP(string key) */
|
|
PHP_METHOD(Redis, rPop)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RPOP", redis_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::blPop(string key1, string key2, ..., int timeout) */
|
|
PHP_METHOD(Redis, blPop)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("BLPOP", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::brPop(string key1, string key2, ..., int timeout) */
|
|
PHP_METHOD(Redis, brPop)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("BRPOP", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto int Redis::lLen(string key) */
|
|
PHP_METHOD(Redis, lLen)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LLEN", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::lrem(string list, string value, int count = 0) */
|
|
PHP_METHOD(Redis, lrem)
|
|
{
|
|
REDIS_PROCESS_CMD(lrem, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::ltrim(string key , int start , int end) */
|
|
PHP_METHOD(Redis, ltrim)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd,
|
|
redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::lindex(string key , int index) */
|
|
PHP_METHOD(Redis, lindex)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::lrange(string key, int start , int end) */
|
|
PHP_METHOD(Redis, lrange)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd,
|
|
redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::sAdd(string key , mixed value) */
|
|
PHP_METHOD(Redis, sAdd)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::sAddArray(string key, array $values) */
|
|
PHP_METHOD(Redis, sAddArray) {
|
|
REDIS_PROCESS_KW_CMD("SADD", redis_key_val_arr_cmd, redis_long_response);
|
|
} /* }}} */
|
|
|
|
/* {{{ proto int Redis::scard(string key) */
|
|
PHP_METHOD(Redis, scard)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SCARD", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::srem(string set, string value) */
|
|
PHP_METHOD(Redis, srem)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::sMove(string src, string dst, mixed value) */
|
|
PHP_METHOD(Redis, sMove)
|
|
{
|
|
REDIS_PROCESS_CMD(smove, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::sPop(string key) */
|
|
PHP_METHOD(Redis, sPop)
|
|
{
|
|
if (ZEND_NUM_ARGS() == 1) {
|
|
REDIS_PROCESS_KW_CMD("SPOP", redis_key_cmd, redis_string_response);
|
|
} else if (ZEND_NUM_ARGS() == 2) {
|
|
REDIS_PROCESS_KW_CMD("SPOP", redis_key_long_cmd, redis_sock_read_multibulk_reply);
|
|
} else {
|
|
ZEND_WRONG_PARAM_COUNT();
|
|
}
|
|
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::sRandMember(string key [int count]) */
|
|
PHP_METHOD(Redis, sRandMember)
|
|
{
|
|
char *cmd;
|
|
int cmd_len;
|
|
short have_count;
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab our socket, validate call
|
|
if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL ||
|
|
redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
|
|
&cmd, &cmd_len, NULL, NULL, &have_count) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
if(have_count) {
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
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);
|
|
} else {
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
|
|
NULL, NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_string_response);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::sismember(string set, string value) */
|
|
PHP_METHOD(Redis, sismember)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sMembers(string set) */
|
|
PHP_METHOD(Redis, sMembers)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd,
|
|
redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sInter(string key0, ... string keyN) */
|
|
PHP_METHOD(Redis, sInter) {
|
|
REDIS_PROCESS_CMD(sinter, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sInterStore(string dst, string key0,...string keyN) */
|
|
PHP_METHOD(Redis, sInterStore) {
|
|
REDIS_PROCESS_CMD(sinterstore, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sUnion(string key0, ... string keyN) */
|
|
PHP_METHOD(Redis, sUnion) {
|
|
REDIS_PROCESS_CMD(sunion, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sUnionStore(string dst, string key0, ... keyN) */
|
|
PHP_METHOD(Redis, sUnionStore) {
|
|
REDIS_PROCESS_CMD(sunionstore, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sDiff(string key0, ... string keyN) */
|
|
PHP_METHOD(Redis, sDiff) {
|
|
REDIS_PROCESS_CMD(sdiff, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::sDiffStore(string dst, string key0, ... keyN) */
|
|
PHP_METHOD(Redis, sDiffStore) {
|
|
REDIS_PROCESS_CMD(sdiffstore, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto array Redis::sort(string key, array options) */
|
|
PHP_METHOD(Redis, sort) {
|
|
char *cmd;
|
|
int cmd_len, have_store;
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab socket, handle command construction
|
|
if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL ||
|
|
redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &have_store,
|
|
&cmd, &cmd_len, NULL, NULL) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock, NULL, NULL) < 0)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
|
|
}
|
|
|
|
static void
|
|
generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha)
|
|
{
|
|
zval *object, *zele, *zget = NULL;
|
|
RedisSock *redis_sock;
|
|
zend_string *zpattern;
|
|
char *key = NULL, *pattern = NULL, *store = NULL;
|
|
size_t keylen, patternlen, storelen;
|
|
zend_long offset = -1, count = -1;
|
|
int argc = 1; /* SORT key is the simplest SORT command */
|
|
smart_string cmd = {0};
|
|
|
|
/* Parse myriad of sort arguments */
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"Os|s!z!lls", &object, redis_ce, &key,
|
|
&keylen, &pattern, &patternlen, &zget,
|
|
&offset, &count, &store, &storelen)
|
|
== FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Ensure we're sorting something, and we can get context */
|
|
if (keylen == 0 || !(redis_sock = redis_sock_get(object, 0)))
|
|
RETURN_FALSE;
|
|
|
|
/* Start calculating argc depending on input arguments */
|
|
if (pattern && patternlen) argc += 2; /* BY pattern */
|
|
if (offset >= 0 && count >= 0) argc += 3; /* LIMIT offset count */
|
|
if (alpha) argc += 1; /* ALPHA */
|
|
if (store) argc += 2; /* STORE destination */
|
|
if (desc) argc += 1; /* DESC (ASC is the default) */
|
|
|
|
/* GET is special. It can be 0 .. N arguments depending what we have */
|
|
if (zget) {
|
|
if (Z_TYPE_P(zget) == IS_ARRAY)
|
|
argc += zend_hash_num_elements(Z_ARRVAL_P(zget));
|
|
else if (Z_STRLEN_P(zget) > 0) {
|
|
argc += 2; /* GET pattern */
|
|
}
|
|
}
|
|
|
|
/* Start constructing final command and append key */
|
|
redis_cmd_init_sstr(&cmd, argc, "SORT", 4);
|
|
redis_cmd_append_sstr_key(&cmd, key, keylen, redis_sock, NULL);
|
|
|
|
/* BY pattern */
|
|
if (pattern && patternlen) {
|
|
redis_cmd_append_sstr(&cmd, "BY", sizeof("BY") - 1);
|
|
redis_cmd_append_sstr(&cmd, pattern, patternlen);
|
|
}
|
|
|
|
/* LIMIT offset count */
|
|
if (offset >= 0 && count >= 0) {
|
|
redis_cmd_append_sstr(&cmd, "LIMIT", sizeof("LIMIT") - 1);
|
|
redis_cmd_append_sstr_long(&cmd, offset);
|
|
redis_cmd_append_sstr_long(&cmd, count);
|
|
}
|
|
|
|
/* Handle any number of GET pattern arguments we've been passed */
|
|
if (zget != NULL) {
|
|
if (Z_TYPE_P(zget) == IS_ARRAY) {
|
|
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zget), zele) {
|
|
zpattern = zval_get_string(zele);
|
|
redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1);
|
|
redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern));
|
|
zend_string_release(zpattern);
|
|
} ZEND_HASH_FOREACH_END();
|
|
} else {
|
|
zpattern = zval_get_string(zget);
|
|
redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1);
|
|
redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern));
|
|
zend_string_release(zpattern);
|
|
}
|
|
}
|
|
|
|
/* Append optional DESC and ALPHA modifiers */
|
|
if (desc) redis_cmd_append_sstr(&cmd, "DESC", sizeof("DESC") - 1);
|
|
if (alpha) redis_cmd_append_sstr(&cmd, "ALPHA", sizeof("ALPHA") - 1);
|
|
|
|
/* Finally append STORE if we've got it */
|
|
if (store && storelen) {
|
|
redis_cmd_append_sstr(&cmd, "STORE", sizeof("STORE") - 1);
|
|
redis_cmd_append_sstr_key(&cmd, store, storelen, redis_sock, NULL);
|
|
}
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock, NULL, NULL) < 0)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_read_variant_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, 0, 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, 0, 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, 1, 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, 1, 1);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::expire(string key, int timeout) */
|
|
PHP_METHOD(Redis, expire) {
|
|
REDIS_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::pexpire(string key, long ms) */
|
|
PHP_METHOD(Redis, pexpire) {
|
|
REDIS_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::expireAt(string key, int timestamp) */
|
|
PHP_METHOD(Redis, expireAt) {
|
|
REDIS_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::pexpireAt(string key, int timestamp) */
|
|
PHP_METHOD(Redis, pexpireAt) {
|
|
REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::lSet(string key, int index, string value) */
|
|
PHP_METHOD(Redis, lSet) {
|
|
REDIS_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd,
|
|
redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::save() */
|
|
PHP_METHOD(Redis, save)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("SAVE", redis_empty_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::bgSave() */
|
|
PHP_METHOD(Redis, bgSave)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("BGSAVE", redis_empty_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto integer Redis::lastSave() */
|
|
PHP_METHOD(Redis, lastSave)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("LASTSAVE", redis_empty_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::flushDB([bool async]) */
|
|
PHP_METHOD(Redis, flushDB)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("FLUSHDB", redis_flush_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::flushAll([bool async]) */
|
|
PHP_METHOD(Redis, flushAll)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("FLUSHALL", redis_flush_cmd, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int Redis::dbSize() */
|
|
PHP_METHOD(Redis, dbSize)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("DBSIZE", redis_empty_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::auth(string passwd) */
|
|
PHP_METHOD(Redis, auth) {
|
|
REDIS_PROCESS_CMD(auth, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::persist(string key) */
|
|
PHP_METHOD(Redis, persist) {
|
|
REDIS_PROCESS_KW_CMD("PERSIST", redis_key_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto long Redis::ttl(string key) */
|
|
PHP_METHOD(Redis, ttl) {
|
|
REDIS_PROCESS_KW_CMD("TTL", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::pttl(string key) */
|
|
PHP_METHOD(Redis, pttl) {
|
|
REDIS_PROCESS_KW_CMD("PTTL", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::info() */
|
|
PHP_METHOD(Redis, info) {
|
|
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
char *cmd, *opt = NULL;
|
|
size_t opt_len;
|
|
int cmd_len;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"O|s", &object, redis_ce, &opt, &opt_len)
|
|
== FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Build a standalone INFO command or one with an option */
|
|
if (opt != NULL) {
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "INFO", "s", opt, opt_len);
|
|
} else {
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "INFO", "");
|
|
}
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL,
|
|
NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_info_response);
|
|
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::select(long dbNumber) */
|
|
PHP_METHOD(Redis, select) {
|
|
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
|
|
char *cmd;
|
|
int cmd_len;
|
|
zend_long dbNumber;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol",
|
|
&object, redis_ce, &dbNumber) == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (dbNumber < 0 || (redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_sock->dbNumber = dbNumber;
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "SELECT", "d", dbNumber);
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
|
|
NULL, NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::swapdb(long srcdb, long dstdb) */
|
|
PHP_METHOD(Redis, swapdb) {
|
|
REDIS_PROCESS_KW_CMD("SWAPDB", redis_long_long_cmd, redis_boolean_response);
|
|
}
|
|
|
|
/* {{{ proto bool Redis::move(string key, long dbindex) */
|
|
PHP_METHOD(Redis, move) {
|
|
REDIS_PROCESS_KW_CMD("MOVE", redis_key_long_cmd, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
static
|
|
void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun)
|
|
{
|
|
RedisSock *redis_sock;
|
|
smart_string cmd = {0};
|
|
zval *object, *z_array;
|
|
HashTable *htargs;
|
|
zend_string *zkey;
|
|
zval *zmem;
|
|
char buf[64];
|
|
size_t keylen;
|
|
zend_ulong idx;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa",
|
|
&object, redis_ce, &z_array) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Make sure we can get our socket, and we were not passed an empty array */
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL ||
|
|
zend_hash_num_elements(Z_ARRVAL_P(z_array)) == 0)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Initialize our command */
|
|
htargs = Z_ARRVAL_P(z_array);
|
|
redis_cmd_init_sstr(&cmd, zend_hash_num_elements(htargs) * 2, kw, strlen(kw));
|
|
|
|
ZEND_HASH_FOREACH_KEY_VAL(htargs, idx, zkey, zmem) {
|
|
/* Handle string or numeric keys */
|
|
if (zkey) {
|
|
redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, NULL);
|
|
} else {
|
|
keylen = snprintf(buf, sizeof(buf), "%ld", (long)idx);
|
|
redis_cmd_append_sstr_key(&cmd, buf, keylen, redis_sock, NULL);
|
|
}
|
|
|
|
/* Append our value */
|
|
redis_cmd_append_sstr_zval(&cmd, zmem, redis_sock);
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
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);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::rpoplpush(string srckey, string dstkey) */
|
|
PHP_METHOD(Redis, rpoplpush)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::brpoplpush(string src, string dst, int timeout) */
|
|
PHP_METHOD(Redis, brpoplpush) {
|
|
REDIS_PROCESS_CMD(brpoplpush, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zAdd(string key, int score, string value) */
|
|
PHP_METHOD(Redis, zAdd) {
|
|
REDIS_PROCESS_CMD(zadd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* Handle ZRANGE and ZREVRANGE as they're the same except for keyword */
|
|
static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw,
|
|
zrange_cb fun)
|
|
{
|
|
char *cmd;
|
|
int cmd_len;
|
|
RedisSock *redis_sock;
|
|
int withscores = 0;
|
|
|
|
if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd,
|
|
&cmd_len, &withscores, NULL, NULL) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
if(withscores) {
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl);
|
|
} else {
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
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::zRange(string key,int start,int end,bool scores = 0) */
|
|
PHP_METHOD(Redis, zRange)
|
|
{
|
|
generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE",
|
|
redis_zrange_cmd);
|
|
}
|
|
|
|
/* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores = 0) */
|
|
PHP_METHOD(Redis, zRevRange) {
|
|
generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE",
|
|
redis_zrange_cmd);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zRangeByScore(string k,string s,string e,array opt) */
|
|
PHP_METHOD(Redis, zRangeByScore) {
|
|
generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE",
|
|
redis_zrangebyscore_cmd);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zRevRangeByScore(string key, string start, string end,
|
|
* array options) */
|
|
PHP_METHOD(Redis, zRevRangeByScore) {
|
|
generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE",
|
|
redis_zrangebyscore_cmd);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zRangeByLex(string key, string min, string max, [
|
|
* offset, limit]) */
|
|
PHP_METHOD(Redis, zRangeByLex) {
|
|
REDIS_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd,
|
|
redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_METHOD(Redis, zRevRangeByLex) {
|
|
REDIS_PROCESS_KW_CMD("ZREVRANGEBYLEX", redis_zrangebylex_cmd,
|
|
redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zLexCount(string key, string min, string max) */
|
|
PHP_METHOD(Redis, zLexCount) {
|
|
REDIS_PROCESS_KW_CMD("ZLEXCOUNT", redis_gen_zlex_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRemRangeByLex(string key, string min, string max) */
|
|
PHP_METHOD(Redis, zRemRangeByLex) {
|
|
REDIS_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd,
|
|
redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRem(string key, string member) */
|
|
PHP_METHOD(Redis, zRem)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRemRangeByScore(string k, string s, string e) */
|
|
PHP_METHOD(Redis, zRemRangeByScore)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd,
|
|
redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRemRangeByRank(string key, long start, long end) */
|
|
PHP_METHOD(Redis, zRemRangeByRank)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd,
|
|
redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zCount(string key, string start , string end) */
|
|
PHP_METHOD(Redis, zCount)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZCOUNT", redis_key_str_str_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zCard(string key) */
|
|
PHP_METHOD(Redis, zCard)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZCARD", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto double Redis::zScore(string key, mixed member) */
|
|
PHP_METHOD(Redis, zScore)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd,
|
|
redis_bulk_double_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRank(string key, string member) */
|
|
PHP_METHOD(Redis, zRank) {
|
|
REDIS_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::zRevRank(string key, string member) */
|
|
PHP_METHOD(Redis, zRevRank) {
|
|
REDIS_PROCESS_KW_CMD("ZREVRANK", redis_kv_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto double Redis::zIncrBy(string key, double value, mixed member) */
|
|
PHP_METHOD(Redis, zIncrBy)
|
|
{
|
|
REDIS_PROCESS_CMD(zincrby, redis_bulk_double_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* zinterstore */
|
|
PHP_METHOD(Redis, zinterstore) {
|
|
REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, redis_long_response);
|
|
}
|
|
|
|
/* zunionstore */
|
|
PHP_METHOD(Redis, zunionstore) {
|
|
REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto array Redis::zPopMax(string key) */
|
|
PHP_METHOD(Redis, zPopMax)
|
|
{
|
|
if (ZEND_NUM_ARGS() == 1) {
|
|
REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, redis_mbulk_reply_zipped_keys_dbl);
|
|
} else if (ZEND_NUM_ARGS() == 2) {
|
|
REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl);
|
|
} else {
|
|
ZEND_WRONG_PARAM_COUNT();
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::zPopMin(string key) */
|
|
PHP_METHOD(Redis, zPopMin)
|
|
{
|
|
if (ZEND_NUM_ARGS() == 1) {
|
|
REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, redis_mbulk_reply_zipped_keys_dbl);
|
|
} else if (ZEND_NUM_ARGS() == 2) {
|
|
REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl);
|
|
} else {
|
|
ZEND_WRONG_PARAM_COUNT();
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::bzPopMax(Array(keys) [, timeout]): Array */
|
|
PHP_METHOD(Redis, bzPopMax) {
|
|
REDIS_PROCESS_KW_CMD("BZPOPMAX", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::bzPopMin(Array(keys) [, timeout]): Array */
|
|
PHP_METHOD(Redis, bzPopMin) {
|
|
REDIS_PROCESS_KW_CMD("BZPOPMIN", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* hashes */
|
|
|
|
/* {{{ proto long Redis::hset(string key, string mem, string val) */
|
|
PHP_METHOD(Redis, hSet)
|
|
{
|
|
REDIS_PROCESS_CMD(hset, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::hSetNx(string key, string mem, string val) */
|
|
PHP_METHOD(Redis, hSetNx)
|
|
{
|
|
REDIS_PROCESS_CMD(hsetnx, redis_1_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::hget(string key, string mem) */
|
|
PHP_METHOD(Redis, hGet)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HGET", redis_key_str_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::hLen(string key) */
|
|
PHP_METHOD(Redis, hLen)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HLEN", redis_key_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::hDel(string key, string mem1, ... memN) */
|
|
PHP_METHOD(Redis, hDel)
|
|
{
|
|
REDIS_PROCESS_CMD(hdel, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::hExists(string key, string mem) */
|
|
PHP_METHOD(Redis, hExists)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HEXISTS", redis_key_str_cmd, redis_1_response);
|
|
}
|
|
|
|
/* {{{ proto array Redis::hkeys(string key) */
|
|
PHP_METHOD(Redis, hKeys)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HKEYS", redis_key_cmd, redis_mbulk_reply_raw);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::hvals(string key) */
|
|
PHP_METHOD(Redis, hVals)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("HVALS", redis_key_cmd,
|
|
redis_sock_read_multibulk_reply);
|
|
}
|
|
|
|
/* {{{ proto array Redis::hgetall(string key) */
|
|
PHP_METHOD(Redis, hGetAll) {
|
|
REDIS_PROCESS_KW_CMD("HGETALL", redis_key_cmd, redis_mbulk_reply_zipped_vals);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto double Redis::hIncrByFloat(string k, string me, double v) */
|
|
PHP_METHOD(Redis, hIncrByFloat)
|
|
{
|
|
REDIS_PROCESS_CMD(hincrbyfloat, redis_bulk_double_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::hincrby(string key, string mem, long byval) */
|
|
PHP_METHOD(Redis, hIncrBy)
|
|
{
|
|
REDIS_PROCESS_CMD(hincrby, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ array Redis::hMget(string hash, array keys) */
|
|
PHP_METHOD(Redis, hMget) {
|
|
REDIS_PROCESS_CMD(hmget, redis_mbulk_reply_assoc);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Redis::hmset(string key, array keyvals) */
|
|
PHP_METHOD(Redis, hMset)
|
|
{
|
|
REDIS_PROCESS_CMD(hmset, redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto long Redis::hstrlen(string key, string field) */
|
|
PHP_METHOD(Redis, hStrLen) {
|
|
REDIS_PROCESS_CMD(hstrlen, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* flag : get, set {ATOMIC, MULTI, PIPELINE} */
|
|
|
|
PHP_METHOD(Redis, multi)
|
|
{
|
|
|
|
RedisSock *redis_sock;
|
|
char *resp, *cmd;
|
|
int resp_len, cmd_len;
|
|
zval *object;
|
|
zend_long multi_value = MULTI;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"O|l", &object, redis_ce, &multi_value)
|
|
== FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* if the flag is activated, send the command, the reply will be "QUEUED"
|
|
* or -ERR */
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (multi_value == PIPELINE) {
|
|
/* Cannot enter pipeline mode in a MULTI block */
|
|
if (IS_MULTI(redis_sock)) {
|
|
php_error_docref(NULL, E_ERROR, "Can't activate pipeline in multi mode!");
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Enable PIPELINE if we're not already in one */
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
free_reply_callbacks(redis_sock);
|
|
REDIS_ENABLE_MODE(redis_sock, PIPELINE);
|
|
}
|
|
} else if (multi_value == MULTI) {
|
|
/* Don't want to do anything if we're already in MULTI mode */
|
|
if (!IS_MULTI(redis_sock)) {
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "MULTI", "");
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len);
|
|
efree(cmd);
|
|
REDIS_SAVE_CALLBACK(NULL, NULL);
|
|
REDIS_ENABLE_MODE(redis_sock, MULTI);
|
|
} else {
|
|
SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len)
|
|
efree(cmd);
|
|
if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) {
|
|
RETURN_FALSE;
|
|
} else if (strncmp(resp, "+OK", 3) != 0) {
|
|
efree(resp);
|
|
RETURN_FALSE;
|
|
}
|
|
efree(resp);
|
|
REDIS_ENABLE_MODE(redis_sock, MULTI);
|
|
}
|
|
}
|
|
} else {
|
|
php_error_docref(NULL, E_WARNING, "Unknown mode sent to Redis::multi");
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
RETURN_ZVAL(getThis(), 1, 0);
|
|
}
|
|
|
|
/* discard */
|
|
PHP_METHOD(Redis, discard)
|
|
{
|
|
int ret = FAILURE;
|
|
RedisSock *redis_sock;
|
|
zval *object;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
|
|
&object, redis_ce) == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
ret = SUCCESS;
|
|
if (redis_sock->pipeline_cmd) {
|
|
zend_string_release(redis_sock->pipeline_cmd);
|
|
redis_sock->pipeline_cmd = NULL;
|
|
}
|
|
} else if (IS_MULTI(redis_sock)) {
|
|
ret = redis_send_discard(redis_sock);
|
|
}
|
|
if (ret == SUCCESS) {
|
|
free_reply_callbacks(redis_sock);
|
|
redis_sock->mode = ATOMIC;
|
|
RETURN_TRUE;
|
|
}
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* redis_sock_read_multibulk_multi_reply */
|
|
PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS,
|
|
RedisSock *redis_sock)
|
|
{
|
|
|
|
char inbuf[4096];
|
|
int numElems;
|
|
size_t len;
|
|
|
|
if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
|
|
return - 1;
|
|
}
|
|
|
|
/* number of responses */
|
|
numElems = atoi(inbuf+1);
|
|
|
|
if(numElems < 0) {
|
|
return -1;
|
|
}
|
|
|
|
array_init(return_value);
|
|
|
|
redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock, return_value, numElems);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* exec */
|
|
PHP_METHOD(Redis, exec)
|
|
{
|
|
RedisSock *redis_sock;
|
|
char *cmd;
|
|
int cmd_len, ret;
|
|
zval *object;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"O", &object, redis_ce) == FAILURE ||
|
|
(redis_sock = redis_sock_get(object, 0)) == NULL
|
|
) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (IS_MULTI(redis_sock)) {
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "EXEC", "");
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len);
|
|
efree(cmd);
|
|
REDIS_SAVE_CALLBACK(NULL, NULL);
|
|
REDIS_DISABLE_MODE(redis_sock, MULTI);
|
|
RETURN_ZVAL(getThis(), 1, 0);
|
|
}
|
|
SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len)
|
|
efree(cmd);
|
|
|
|
ret = redis_sock_read_multibulk_multi_reply(
|
|
INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
|
|
free_reply_callbacks(redis_sock);
|
|
REDIS_DISABLE_MODE(redis_sock, MULTI);
|
|
redis_sock->watching = 0;
|
|
if (ret < 0) {
|
|
zval_dtor(return_value);
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
if (redis_sock->pipeline_cmd == NULL) {
|
|
/* Empty array when no command was run. */
|
|
array_init(return_value);
|
|
} else {
|
|
if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd),
|
|
ZSTR_LEN(redis_sock->pipeline_cmd)) < 0) {
|
|
ZVAL_FALSE(return_value);
|
|
} else {
|
|
array_init(return_value);
|
|
redis_sock_read_multibulk_multi_reply_loop(
|
|
INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, return_value, 0);
|
|
}
|
|
zend_string_release(redis_sock->pipeline_cmd);
|
|
redis_sock->pipeline_cmd = NULL;
|
|
}
|
|
free_reply_callbacks(redis_sock);
|
|
REDIS_DISABLE_MODE(redis_sock, PIPELINE);
|
|
}
|
|
}
|
|
|
|
PHP_REDIS_API int
|
|
redis_response_enqueued(RedisSock *redis_sock)
|
|
{
|
|
char *resp;
|
|
int resp_len, ret = FAILURE;
|
|
|
|
if ((resp = redis_sock_read(redis_sock, &resp_len)) != NULL) {
|
|
if (strncmp(resp, "+QUEUED", 7) == 0) {
|
|
ret = SUCCESS;
|
|
}
|
|
efree(resp);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* TODO: Investigate/fix the odd logic going on in here. Looks like previous abort
|
|
* conditions that are now simply empty if { } { } blocks. */
|
|
PHP_REDIS_API int
|
|
redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
|
|
RedisSock *redis_sock, zval *z_tab,
|
|
int numElems)
|
|
{
|
|
fold_item *fi;
|
|
|
|
for (fi = redis_sock->head; fi; /* void */) {
|
|
if (fi->fun) {
|
|
fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, fi->ctx);
|
|
fi = fi->next;
|
|
continue;
|
|
}
|
|
size_t len;
|
|
char inbuf[255];
|
|
|
|
if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
|
|
} else if (strncmp(inbuf, "+OK", 3) != 0) {
|
|
}
|
|
|
|
while ((fi = fi->next) && fi->fun) {
|
|
if (redis_response_enqueued(redis_sock) == SUCCESS) {
|
|
} else {
|
|
}
|
|
}
|
|
|
|
if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
|
|
}
|
|
|
|
zval z_ret;
|
|
array_init(&z_ret);
|
|
add_next_index_zval(z_tab, &z_ret);
|
|
|
|
int num = atol(inbuf + 1);
|
|
|
|
if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) < 0) {
|
|
}
|
|
|
|
if (fi) fi = fi->next;
|
|
}
|
|
redis_sock->current = fi;
|
|
return 0;
|
|
}
|
|
|
|
PHP_METHOD(Redis, pipeline)
|
|
{
|
|
RedisSock *redis_sock;
|
|
zval *object;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"O", &object, redis_ce) == FAILURE ||
|
|
(redis_sock = redis_sock_get(object, 0)) == NULL
|
|
) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* User cannot enter MULTI mode if already in a pipeline */
|
|
if (IS_MULTI(redis_sock)) {
|
|
php_error_docref(NULL, E_ERROR, "Can't activate pipeline in multi mode!");
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Enable pipeline mode unless we're already in that mode in which case this
|
|
* is just a NO OP */
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
/* 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(redis_sock);
|
|
REDIS_ENABLE_MODE(redis_sock, PIPELINE);
|
|
}
|
|
|
|
RETURN_ZVAL(getThis(), 1, 0);
|
|
}
|
|
|
|
/* {{{ proto long Redis::publish(string channel, string msg) */
|
|
PHP_METHOD(Redis, publish)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("PUBLISH", redis_key_str_cmd, redis_long_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void Redis::psubscribe(Array(pattern1, pattern2, ... patternN)) */
|
|
PHP_METHOD(Redis, psubscribe)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd,
|
|
redis_subscribe_response);
|
|
}
|
|
|
|
/* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN)) */
|
|
PHP_METHOD(Redis, subscribe) {
|
|
REDIS_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd,
|
|
redis_subscribe_response);
|
|
}
|
|
|
|
/**
|
|
* [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
|
|
* );
|
|
**/
|
|
|
|
PHP_METHOD(Redis, unsubscribe)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("UNSUBSCRIBE", redis_unsubscribe_cmd,
|
|
redis_unsubscribe_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, punsubscribe)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("PUNSUBSCRIBE", redis_unsubscribe_cmd,
|
|
redis_unsubscribe_response);
|
|
}
|
|
|
|
/* {{{ proto string Redis::bgrewriteaof() */
|
|
PHP_METHOD(Redis, bgrewriteaof)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("BGREWRITEAOF", redis_empty_cmd,
|
|
redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::slaveof([host, port]) */
|
|
PHP_METHOD(Redis, slaveof)
|
|
{
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
char *cmd = "", *host = NULL;
|
|
size_t host_len;
|
|
zend_long port = 6379;
|
|
int cmd_len;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"O|sl", &object, redis_ce, &host,
|
|
&host_len, &port) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
if (port < 0 || (redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (host && host_len) {
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "SLAVEOF", "sd", host, host_len, (int)port);
|
|
} else {
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "SLAVEOF", "ss", "NO", 2, "ONE", 3);
|
|
}
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
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)
|
|
{
|
|
RedisSock *redis_sock;
|
|
char *cmd; int cmd_len;
|
|
REDIS_REPLY_TYPE rtype;
|
|
|
|
if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if(redis_object_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &rtype,
|
|
&cmd, &cmd_len, NULL, NULL)==FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
|
|
if(rtype == TYPE_INT) {
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
|
|
NULL, NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_long_response);
|
|
} else {
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
|
|
NULL, NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_string_response);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::getOption($option) */
|
|
PHP_METHOD(Redis, getOption)
|
|
{
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string Redis::setOption(string $option, mixed $value) */
|
|
PHP_METHOD(Redis, setOption)
|
|
{
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto boolean Redis::config(string op, string key [, mixed value]) */
|
|
PHP_METHOD(Redis, config)
|
|
{
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
char *key = NULL, *val = NULL, *cmd, *op = NULL;
|
|
size_t key_len, val_len, op_len;
|
|
enum {CFG_GET, CFG_SET} mode;
|
|
int cmd_len;
|
|
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), 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 = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (mode == CFG_GET && val == NULL) {
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "ss", op, op_len, key, key_len);
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw);
|
|
|
|
} else if(mode == CFG_SET && val != NULL) {
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len);
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
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 cmd_len;
|
|
size_t arg_len;
|
|
zend_long option = 0;
|
|
enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode;
|
|
|
|
// Make sure we can get parameters
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), 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 = redis_sock_get(object, 0)) == NULL) {
|
|
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_SPPRINTF(&cmd, "SLOWLOG", "sl", arg, arg_len, option);
|
|
} else {
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "SLOWLOG", "s", arg, arg_len);
|
|
}
|
|
|
|
/* Kick off our command */
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock, NULL, NULL) < 0)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto Redis::wait(int num_slaves, int ms) }}} */
|
|
PHP_METHOD(Redis, wait) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
zend_long num_slaves, timeout;
|
|
char *cmd;
|
|
int cmd_len;
|
|
|
|
/* Make sure arguments are valid */
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll",
|
|
&object, redis_ce, &num_slaves, &timeout)
|
|
==FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Don't even send this to Redis if our args are negative */
|
|
if(num_slaves < 0 || timeout < 0) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Grab our socket */
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
// Construct the command
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "WAIT", "ll", num_slaves, timeout);
|
|
|
|
/* Kick it off */
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL,
|
|
NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_long_response);
|
|
}
|
|
|
|
/* Construct a PUBSUB command */
|
|
PHP_REDIS_API int
|
|
redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
|
|
zval *arg)
|
|
{
|
|
HashTable *ht_chan;
|
|
zval *z_ele;
|
|
smart_string cmd = {0};
|
|
|
|
if (type == PUBSUB_CHANNELS) {
|
|
if (arg) {
|
|
/* With a pattern */
|
|
return REDIS_SPPRINTF(ret, "PUBSUB", "sk", "CHANNELS", sizeof("CHANNELS") - 1,
|
|
Z_STRVAL_P(arg), Z_STRLEN_P(arg));
|
|
} else {
|
|
/* No pattern */
|
|
return REDIS_SPPRINTF(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS") - 1);
|
|
}
|
|
} else if (type == PUBSUB_NUMSUB) {
|
|
ht_chan = Z_ARRVAL_P(arg);
|
|
|
|
// Add PUBSUB and NUMSUB bits
|
|
redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1);
|
|
redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1);
|
|
|
|
/* Iterate our elements */
|
|
ZEND_HASH_FOREACH_VAL(ht_chan, z_ele) {
|
|
zend_string *zstr = zval_get_string(z_ele);
|
|
redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, NULL);
|
|
zend_string_release(zstr);
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
/* Set return */
|
|
*ret = cmd.c;
|
|
return cmd.len;
|
|
} else if (type == PUBSUB_NUMPAT) {
|
|
return REDIS_SPPRINTF(ret, "PUBSUB", "s", "NUMPAT", sizeof("NUMPAT") - 1);
|
|
}
|
|
|
|
/* Shouldn't ever happen */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* {{{ proto Redis::pubsub("channels", pattern);
|
|
* proto Redis::pubsub("numsub", Array channels);
|
|
* proto Redis::pubsub("numpat"); }}}
|
|
*/
|
|
PHP_METHOD(Redis, pubsub) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
char *keyword, *cmd;
|
|
int cmd_len;
|
|
size_t kw_len;
|
|
PUBSUB_TYPE type;
|
|
zval *arg = NULL;
|
|
|
|
// Parse arguments
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"Os|z", &object, redis_ce, &keyword,
|
|
&kw_len, &arg)==FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Validate our sub command keyword, and that we've got proper arguments */
|
|
if(!strncasecmp(keyword, "channels", sizeof("channels"))) {
|
|
/* One (optional) string argument */
|
|
if(arg && Z_TYPE_P(arg) != IS_STRING) {
|
|
RETURN_FALSE;
|
|
}
|
|
type = PUBSUB_CHANNELS;
|
|
} else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) {
|
|
/* One array argument */
|
|
if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY ||
|
|
zend_hash_num_elements(Z_ARRVAL_P(arg)) == 0)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
type = PUBSUB_NUMSUB;
|
|
} else if(!strncasecmp(keyword, "numpat", sizeof("numpat"))) {
|
|
type = PUBSUB_NUMPAT;
|
|
} else {
|
|
/* Invalid keyword */
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Grab our socket context object */
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Construct our "PUBSUB" command */
|
|
cmd_len = redis_build_pubsub_cmd(redis_sock, &cmd, type, arg);
|
|
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
|
|
if(type == PUBSUB_NUMSUB) {
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock, NULL, NULL) < 0)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_int);
|
|
} else {
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock, NULL, NULL) < 0)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
|
|
}
|
|
}
|
|
|
|
/* {{{ proto variant Redis::eval(string script, [array keys, long num_keys]) */
|
|
PHP_METHOD(Redis, eval)
|
|
{
|
|
REDIS_PROCESS_KW_CMD("EVAL", redis_eval_cmd, redis_read_raw_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto variant Redis::evalsha(string sha1, [array keys, long num_keys]) */
|
|
PHP_METHOD(Redis, evalsha) {
|
|
REDIS_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, redis_read_raw_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto 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;
|
|
smart_string cmd = {0};
|
|
int argc = ZEND_NUM_ARGS();
|
|
|
|
/* Attempt to grab our socket */
|
|
if (argc < 1 || (redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Allocate an array big enough to store our arguments */
|
|
z_args = ecalloc(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 ||
|
|
redis_build_script_cmd(&cmd, argc, z_args) == NULL
|
|
) {
|
|
efree(z_args);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Free our allocated arguments */
|
|
efree(z_args);
|
|
|
|
// Kick off our request
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock, NULL, NULL) < 0)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
|
|
}
|
|
|
|
/* {{{ proto DUMP key */
|
|
PHP_METHOD(Redis, dump) {
|
|
REDIS_PROCESS_KW_CMD("DUMP", redis_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::restore(ttl, key, value) */
|
|
PHP_METHOD(Redis, restore) {
|
|
REDIS_PROCESS_KW_CMD("RESTORE", redis_key_long_val_cmd,
|
|
redis_boolean_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::debug(string key) */
|
|
PHP_METHOD(Redis, debug) {
|
|
REDIS_PROCESS_KW_CMD("DEBUG", redis_key_cmd, redis_string_response);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto Redis::migrate(host port key dest-db timeout [bool copy,
|
|
* bool replace]) */
|
|
PHP_METHOD(Redis, migrate) {
|
|
REDIS_PROCESS_CMD(migrate, redis_boolean_response);
|
|
}
|
|
|
|
/* {{{ proto Redis::_prefix(key) */
|
|
PHP_METHOD(Redis, _prefix) {
|
|
RedisSock *redis_sock;
|
|
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
|
|
}
|
|
|
|
/* {{{ proto Redis::_serialize(value) */
|
|
PHP_METHOD(Redis, _serialize) {
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_serialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
|
|
}
|
|
|
|
/* {{{ proto Redis::_unserialize(value) */
|
|
PHP_METHOD(Redis, _unserialize) {
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
redis_unserialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
|
|
redis_exception_ce);
|
|
}
|
|
|
|
/* {{{ proto Redis::getLastError() */
|
|
PHP_METHOD(Redis, getLastError) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab our object
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
|
|
&object, redis_ce) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Return our last error or NULL if we don't have one */
|
|
if (redis_sock->err) {
|
|
RETURN_STRINGL(ZSTR_VAL(redis_sock->err), ZSTR_LEN(redis_sock->err));
|
|
}
|
|
RETURN_NULL();
|
|
}
|
|
|
|
/* {{{ proto Redis::clearLastError() */
|
|
PHP_METHOD(Redis, clearLastError) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
|
|
// Grab our object
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
|
|
&object, redis_ce) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
// Grab socket
|
|
if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
// Clear error message
|
|
if (redis_sock->err) {
|
|
zend_string_release(redis_sock->err);
|
|
redis_sock->err = NULL;
|
|
}
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
|
|
/*
|
|
* {{{ proto long Redis::getMode()
|
|
*/
|
|
PHP_METHOD(Redis, getMode) {
|
|
zval *object;
|
|
RedisSock *redis_sock;
|
|
|
|
/* Grab our object */
|
|
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Grab socket */
|
|
if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (IS_PIPELINE(redis_sock)) {
|
|
RETVAL_LONG(PIPELINE);
|
|
} else if (IS_MULTI(redis_sock)) {
|
|
RETVAL_LONG(MULTI);
|
|
} else {
|
|
RETVAL_LONG(ATOMIC);
|
|
}
|
|
}
|
|
|
|
/* {{{ proto Redis::time() */
|
|
PHP_METHOD(Redis, time) {
|
|
REDIS_PROCESS_KW_CMD("TIME", redis_empty_cmd, redis_mbulk_reply_raw);
|
|
}
|
|
|
|
/* {{{ proto array Redis::role() */
|
|
PHP_METHOD(Redis, role) {
|
|
REDIS_PROCESS_KW_CMD("ROLE", redis_empty_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
/*
|
|
* Introspection stuff
|
|
*/
|
|
|
|
/* {{{ proto Redis::IsConnected */
|
|
PHP_METHOD(Redis, isConnected) {
|
|
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_STRINGL(ZSTR_VAL(redis_sock->host), ZSTR_LEN(redis_sock->host));
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
/* {{{ proto Redis::getPort() */
|
|
PHP_METHOD(Redis, getPort) {
|
|
RedisSock *redis_sock;
|
|
|
|
if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
|
|
/* Return our port */
|
|
RETURN_LONG(redis_sock->port);
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
/* {{{ 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)) == NULL) {
|
|
RETURN_FALSE;
|
|
} else if (redis_sock->persistent_id == NULL) {
|
|
RETURN_NULL();
|
|
}
|
|
RETURN_STRINGL(ZSTR_VAL(redis_sock->persistent_id), ZSTR_LEN(redis_sock->persistent_id));
|
|
}
|
|
|
|
/* {{{ proto Redis::getAuth */
|
|
PHP_METHOD(Redis, getAuth) {
|
|
RedisSock *redis_sock;
|
|
zval zret;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE)
|
|
RETURN_FALSE;
|
|
|
|
redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU);
|
|
if (redis_sock == NULL)
|
|
RETURN_FALSE;
|
|
|
|
if (redis_sock->user && redis_sock->pass) {
|
|
array_init(&zret);
|
|
add_next_index_str(&zret, zend_string_copy(redis_sock->user));
|
|
add_next_index_str(&zret, zend_string_copy(redis_sock->pass));
|
|
RETURN_ZVAL(&zret, 0, 0);
|
|
} else if (redis_sock->pass) {
|
|
RETURN_STR_COPY(redis_sock->pass);
|
|
} else {
|
|
RETURN_NULL();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* $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;
|
|
size_t opt_len, arg_len;
|
|
int cmd_len;
|
|
|
|
// Parse our method parameters
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"Os|s", &object, redis_ce, &opt, &opt_len,
|
|
&arg, &arg_len) == FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Grab our socket */
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Build our CLIENT command */
|
|
if (ZEND_NUM_ARGS() == 2) {
|
|
cmd_len = REDIS_SPPRINTF(&cmd, "CLIENT", "ss", opt, opt_len, arg, arg_len);
|
|
} else {
|
|
cmd_len = REDIS_SPPRINTF(&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 (IS_ATOMIC(redis_sock)) {
|
|
redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,
|
|
NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_client_list_reply);
|
|
} else {
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock,NULL,NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
|
|
}
|
|
}
|
|
|
|
/* {{{ proto mixed Redis::rawcommand(string $command, [ $arg1 ... $argN]) */
|
|
PHP_METHOD(Redis, rawcommand) {
|
|
int argc = ZEND_NUM_ARGS(), cmd_len;
|
|
char *cmd = NULL;
|
|
RedisSock *redis_sock;
|
|
zval *z_args;
|
|
|
|
/* Sanity check on arguments */
|
|
if (argc < 1) {
|
|
php_error_docref(NULL, E_WARNING,
|
|
"Must pass at least one command keyword");
|
|
RETURN_FALSE;
|
|
}
|
|
z_args = emalloc(argc * sizeof(zval));
|
|
if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
|
|
php_error_docref(NULL, E_WARNING,
|
|
"Internal PHP error parsing arguments");
|
|
efree(z_args);
|
|
RETURN_FALSE;
|
|
} else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len) < 0 ||
|
|
(redis_sock = redis_sock_get(getThis(), 0)) == NULL
|
|
) {
|
|
if (cmd) efree(cmd);
|
|
efree(z_args);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Clean up command array */
|
|
efree(z_args);
|
|
|
|
/* Execute our command */
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
if (IS_ATOMIC(redis_sock)) {
|
|
redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL,NULL);
|
|
}
|
|
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array Redis::command()
|
|
* proto array Redis::command('info', string cmd)
|
|
* proto array Redis::command('getkeys', array cmd_args) */
|
|
PHP_METHOD(Redis, command) {
|
|
REDIS_PROCESS_CMD(command, redis_read_variant_reply);
|
|
}
|
|
/* }}} */
|
|
|
|
/* Helper to format any combination of SCAN arguments */
|
|
PHP_REDIS_API int
|
|
redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
|
|
long iter, char *pattern, int pattern_len, int count,
|
|
zend_string *match_type)
|
|
{
|
|
smart_string cmdstr = {0};
|
|
char *keyword;
|
|
int argc;
|
|
|
|
/* Count our arguments +1 for key if it's got one, and + 2 for pattern */
|
|
/* or count given that they each carry keywords with them. */
|
|
argc = 1 + (key_len > 0) + (pattern_len > 0 ? 2 : 0) + (count > 0 ? 2 : 0) + (match_type ? 2 : 0);
|
|
|
|
/* Turn our type into a keyword */
|
|
switch(type) {
|
|
case TYPE_SCAN:
|
|
keyword = "SCAN";
|
|
break;
|
|
case TYPE_SSCAN:
|
|
keyword = "SSCAN";
|
|
break;
|
|
case TYPE_HSCAN:
|
|
keyword = "HSCAN";
|
|
break;
|
|
case TYPE_ZSCAN:
|
|
default:
|
|
keyword = "ZSCAN";
|
|
break;
|
|
}
|
|
|
|
/* Start the command */
|
|
redis_cmd_init_sstr(&cmdstr, argc, keyword, strlen(keyword));
|
|
if (key_len) redis_cmd_append_sstr(&cmdstr, key, key_len);
|
|
redis_cmd_append_sstr_long(&cmdstr, iter);
|
|
|
|
/* Append COUNT if we've got it */
|
|
if(count) {
|
|
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT");
|
|
redis_cmd_append_sstr_int(&cmdstr, count);
|
|
}
|
|
|
|
/* Append MATCH if we've got it */
|
|
if(pattern_len) {
|
|
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MATCH");
|
|
redis_cmd_append_sstr(&cmdstr, pattern, pattern_len);
|
|
}
|
|
|
|
if (match_type) {
|
|
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TYPE");
|
|
redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(match_type), ZSTR_LEN(match_type));
|
|
}
|
|
|
|
/* Return our command length */
|
|
*cmd = cmdstr.c;
|
|
return cmdstr.len;
|
|
}
|
|
|
|
/* {{{ proto redis::scan(&$iterator, [pattern, [count, [type]]]) */
|
|
PHP_REDIS_API void
|
|
generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
|
|
zval *object, *z_iter;
|
|
RedisSock *redis_sock;
|
|
HashTable *hash;
|
|
char *pattern = NULL, *cmd, *key = NULL;
|
|
int cmd_len, num_elements, key_free = 0, pattern_free = 0;
|
|
size_t key_len = 0, pattern_len = 0;
|
|
zend_string *match_type = NULL;
|
|
zend_long count = 0, iter;
|
|
|
|
/* Different prototype depending on if this is a key based scan */
|
|
if(type != TYPE_SCAN) {
|
|
// Requires a key
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"Osz/|s!l", &object, redis_ce, &key,
|
|
&key_len, &z_iter, &pattern,
|
|
&pattern_len, &count)==FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
} else {
|
|
// Doesn't require a key
|
|
if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
|
|
"Oz/|s!lS", &object, redis_ce, &z_iter,
|
|
&pattern, &pattern_len, &count, &match_type)
|
|
== FAILURE)
|
|
{
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
/* Grab our socket */
|
|
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Calling this in a pipeline makes no sense */
|
|
if (!IS_ATOMIC(redis_sock)) {
|
|
php_error_docref(NULL, E_ERROR,
|
|
"Can't call SCAN commands in multi or pipeline mode!");
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
// The iterator should be passed in as NULL for the first iteration, but we
|
|
// can treat any NON LONG value as NULL for these purposes as we've
|
|
// separated the variable anyway.
|
|
if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter) < 0) {
|
|
/* Convert to long */
|
|
convert_to_long(z_iter);
|
|
iter = 0;
|
|
} else if(Z_LVAL_P(z_iter) != 0) {
|
|
/* Update our iterator value for the next passthru */
|
|
iter = Z_LVAL_P(z_iter);
|
|
} else {
|
|
/* We're done, back to iterator zero */
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Prefix our key if we've got one and we have a prefix set */
|
|
if(key_len) {
|
|
key_free = redis_key_prefix(redis_sock, &key, &key_len);
|
|
}
|
|
|
|
if (redis_sock->scan & REDIS_SCAN_PREFIX) {
|
|
pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len);
|
|
}
|
|
|
|
/**
|
|
* Redis can return to us empty keys, especially in the case where there
|
|
* are a large number of keys to scan, and we're matching against a
|
|
* pattern. phpredis can be set up to abstract this from the user, by
|
|
* setting OPT_SCAN to REDIS_SCAN_RETRY. Otherwise we will return empty
|
|
* keys and the user will need to make subsequent calls with an updated
|
|
* iterator.
|
|
*/
|
|
do {
|
|
/* Free our previous reply if we're back in the loop. We know we are
|
|
* if our return_value is an array */
|
|
if (Z_TYPE_P(return_value) == IS_ARRAY) {
|
|
zval_dtor(return_value);
|
|
ZVAL_NULL(return_value);
|
|
}
|
|
|
|
// Format our SCAN command
|
|
cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (long)iter,
|
|
pattern, pattern_len, count, match_type);
|
|
|
|
/* Execute our command getting our new iterator value */
|
|
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
|
|
if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
|
|
redis_sock,type,&iter) < 0)
|
|
{
|
|
if(key_free) efree(key);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Get the number of elements */
|
|
hash = Z_ARRVAL_P(return_value);
|
|
num_elements = zend_hash_num_elements(hash);
|
|
} while (redis_sock->scan & REDIS_SCAN_RETRY && iter != 0 &&
|
|
num_elements == 0);
|
|
|
|
/* Free our pattern if it was prefixed */
|
|
if (pattern_free) efree(pattern);
|
|
|
|
/* Free our key if it was prefixed */
|
|
if(key_free) efree(key);
|
|
|
|
/* Update our iterator reference */
|
|
Z_LVAL_P(z_iter) = iter;
|
|
}
|
|
|
|
PHP_METHOD(Redis, scan) {
|
|
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SCAN);
|
|
}
|
|
PHP_METHOD(Redis, hscan) {
|
|
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_HSCAN);
|
|
}
|
|
PHP_METHOD(Redis, sscan) {
|
|
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SSCAN);
|
|
}
|
|
PHP_METHOD(Redis, zscan) {
|
|
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN);
|
|
}
|
|
|
|
/*
|
|
* HyperLogLog based commands
|
|
*/
|
|
|
|
/* {{{ proto Redis::pfAdd(string key, array elements) }}} */
|
|
PHP_METHOD(Redis, pfadd) {
|
|
REDIS_PROCESS_CMD(pfadd, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto Redis::pfCount(string key) }}}*/
|
|
PHP_METHOD(Redis, pfcount) {
|
|
REDIS_PROCESS_CMD(pfcount, redis_long_response);
|
|
}
|
|
|
|
/* {{{ proto Redis::pfMerge(string dstkey, array keys) }}}*/
|
|
PHP_METHOD(Redis, pfmerge) {
|
|
REDIS_PROCESS_CMD(pfmerge, redis_boolean_response);
|
|
}
|
|
|
|
/*
|
|
* Geo commands
|
|
*/
|
|
|
|
PHP_METHOD(Redis, geoadd) {
|
|
REDIS_PROCESS_KW_CMD("GEOADD", redis_key_varval_cmd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, geohash) {
|
|
REDIS_PROCESS_KW_CMD("GEOHASH", redis_key_varval_cmd, redis_mbulk_reply_raw);
|
|
}
|
|
|
|
PHP_METHOD(Redis, geopos) {
|
|
REDIS_PROCESS_KW_CMD("GEOPOS", redis_key_varval_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, geodist) {
|
|
REDIS_PROCESS_CMD(geodist, redis_bulk_double_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, georadius) {
|
|
REDIS_PROCESS_KW_CMD("GEORADIUS", redis_georadius_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, georadius_ro) {
|
|
REDIS_PROCESS_KW_CMD("GEORADIUS_RO", redis_georadius_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, georadiusbymember) {
|
|
REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER", redis_georadiusbymember_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, georadiusbymember_ro) {
|
|
REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, redis_read_variant_reply);
|
|
}
|
|
|
|
/*
|
|
* Streams
|
|
*/
|
|
|
|
PHP_METHOD(Redis, xack) {
|
|
REDIS_PROCESS_CMD(xack, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xadd) {
|
|
REDIS_PROCESS_CMD(xadd, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xclaim) {
|
|
REDIS_PROCESS_CMD(xclaim, redis_xclaim_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xdel) {
|
|
REDIS_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xgroup) {
|
|
REDIS_PROCESS_CMD(xgroup, redis_read_variant_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xinfo) {
|
|
REDIS_PROCESS_CMD(xinfo, redis_xinfo_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xlen) {
|
|
REDIS_PROCESS_KW_CMD("XLEN", redis_key_cmd, redis_long_response);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xpending) {
|
|
REDIS_PROCESS_CMD(xpending, redis_read_variant_reply_strings);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xrange) {
|
|
REDIS_PROCESS_KW_CMD("XRANGE", redis_xrange_cmd, redis_xrange_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xread) {
|
|
REDIS_PROCESS_CMD(xread, redis_xread_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xreadgroup) {
|
|
REDIS_PROCESS_CMD(xreadgroup, redis_xread_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xrevrange) {
|
|
REDIS_PROCESS_KW_CMD("XREVRANGE", redis_xrange_cmd, redis_xrange_reply);
|
|
}
|
|
|
|
PHP_METHOD(Redis, xtrim) {
|
|
REDIS_PROCESS_CMD(xtrim, redis_long_response);
|
|
}
|
|
|
|
/* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */
|